@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,161 +0,0 @@
1
- import { describe, it, expect, vi } from 'vitest';
2
- import { SemVer } from 'semver';
3
-
4
- // Mock the version first (hoisted)
5
- vi.mock('../../version', () => ({
6
- version: '6.1.2-next.0+1493919587',
7
- }));
8
-
9
- import { resolveVersion } from '../../versioning/resolve-version';
10
- import { MsalModuleVersion } from '../../static';
11
-
12
- describe('resolveVersion', () => {
13
- describe('successful version resolution', () => {
14
- it('should resolve a valid version string', () => {
15
- const result = resolveVersion('2.1.0');
16
-
17
- expect(result).toMatchObject({
18
- wantedVersion: expect.any(SemVer),
19
- latestVersion: expect.any(SemVer),
20
- isLatest: false,
21
- satisfiesLatest: false, // 2.x is not compatible with 5.x
22
- enumVersion: MsalModuleVersion.V2,
23
- });
24
-
25
- expect(result.wantedVersion.version).toBe('2.1.0');
26
- expect(result.latestVersion.version).toBe('6.1.2');
27
- });
28
-
29
- it('should resolve with SemVer object', () => {
30
- const semver = new SemVer('2.0.0');
31
- const result = resolveVersion(semver);
32
-
33
- expect(result.wantedVersion).toBe(semver);
34
- expect(result.satisfiesLatest).toBe(false); // 2.x is not compatible with 5.x
35
- });
36
-
37
- it('should resolve latest version when no version provided', () => {
38
- const result = resolveVersion();
39
-
40
- expect(result.wantedVersion.version).toBe('6.1.2');
41
- expect(result.latestVersion.version).toBe('6.1.2');
42
- expect(result.isLatest).toBe(true);
43
- expect(result.satisfiesLatest).toBe(true);
44
- });
45
-
46
- it('should resolve latest version when empty string provided', () => {
47
- const result = resolveVersion('');
48
-
49
- expect(result.wantedVersion.version).toBe('6.1.2');
50
- expect(result.isLatest).toBe(true);
51
- });
52
-
53
- it('should find correct enum version for major version', () => {
54
- const result = resolveVersion('2.5.0');
55
-
56
- expect(result.enumVersion).toBe(MsalModuleVersion.V2);
57
- expect(result.satisfiesLatest).toBe(false); // 2.x is not compatible with 5.x
58
- });
59
- });
60
-
61
- describe('version comparison and warnings', () => {
62
- it('should identify version states correctly', () => {
63
- // Exact latest version
64
- expect(resolveVersion('6.1.2-next.0+1493919587').isLatest).toBe(true);
65
-
66
- // Patch difference (satisfies but not latest)
67
- const patchResult = resolveVersion('6.1.1');
68
- expect(patchResult.isLatest).toBe(false);
69
- expect(patchResult.satisfiesLatest).toBe(true);
70
- expect(patchResult.warnings).toBeUndefined();
71
-
72
- // Minor difference (satisfies)
73
- const minorResult = resolveVersion('6.0.0');
74
- expect(minorResult.isLatest).toBe(false);
75
- expect(minorResult.satisfiesLatest).toBe(true);
76
-
77
- // Major difference (doesn't satisfy)
78
- const majorResult = resolveVersion('7.0.0');
79
- expect(majorResult.satisfiesLatest).toBe(false);
80
- });
81
-
82
- it('should handle versions without matching enum with warnings', () => {
83
- // Lower major version - should map to V2
84
- const lowerResult = resolveVersion('3.0.0');
85
- expect(lowerResult.enumVersion).toBe(MsalModuleVersion.V2);
86
- expect(lowerResult.warnings).toBeDefined();
87
- expect(lowerResult.warnings?.[0]).toContain(
88
- 'Requested major version 3 is behind the latest major version 6',
89
- );
90
-
91
- // Zero version - should map to V2
92
- const zeroResult = resolveVersion('0.0.0');
93
- expect(zeroResult.enumVersion).toBe(MsalModuleVersion.V2);
94
- expect(zeroResult.warnings).toBeDefined();
95
- expect(zeroResult.warnings?.[0]).toContain(
96
- 'Requested major version 0 is behind the latest major version 6',
97
- );
98
- });
99
- });
100
-
101
- describe('error handling', () => {
102
- it('should handle invalid version strings gracefully', () => {
103
- const invalidVersions = ['invalid-version', 'not-a-version', 'invalid', 'bad.version'];
104
-
105
- invalidVersions.forEach((invalidVersion) => {
106
- const result = resolveVersion(invalidVersion);
107
- expect(result.warnings).toBeDefined();
108
- expect(result.warnings).toHaveLength(1);
109
- expect(result.warnings?.[0]).toContain(
110
- `Failed to parse requested version "${invalidVersion}"`,
111
- );
112
- // Should fall back to latest version (coerced)
113
- expect(result.wantedVersion.version).toBe('6.1.2');
114
- });
115
- });
116
-
117
- it('should handle malformed but coercible versions', () => {
118
- // semver.coerce handles these gracefully
119
- expect(() => resolveVersion('2.')).not.toThrow();
120
- expect(() => resolveVersion('4')).not.toThrow();
121
- });
122
- });
123
-
124
- describe('edge cases', () => {
125
- it('should handle complex version strings with pre-release and build metadata', () => {
126
- const testCases = [
127
- { input: '6.1.2-next.0+1493919587-beta.1', expected: '6.1.2' },
128
- { input: '6.1.2-next.0+1493919587+build.123', expected: '6.1.2' },
129
- { input: '6.1.2-next.0+1493919587-beta.1.alpha.2+build.123.456', expected: '6.1.2' },
130
- ];
131
-
132
- testCases.forEach(({ input, expected }) => {
133
- const result = resolveVersion(input);
134
- expect(result.satisfiesLatest).toBe(true);
135
- expect(result.wantedVersion.version).toBe(expected);
136
- });
137
- });
138
-
139
- it('should return consistent structure across calls', () => {
140
- const result1 = resolveVersion('2.0.0');
141
- const result2 = resolveVersion('4.0.0');
142
-
143
- // Latest version should be consistent
144
- expect(result1.latestVersion.version).toBe(result2.latestVersion.version);
145
- expect(result1.latestVersion.version).toBe('6.1.2');
146
-
147
- // Structure should be consistent
148
- expect(result1).toHaveProperty('wantedVersion');
149
- expect(result1).toHaveProperty('latestVersion');
150
- expect(result1).toHaveProperty('isLatest');
151
- expect(result1).toHaveProperty('satisfiesLatest');
152
- expect(result1).toHaveProperty('enumVersion');
153
-
154
- expect(result1.wantedVersion).toBeInstanceOf(SemVer);
155
- expect(result1.latestVersion).toBeInstanceOf(SemVer);
156
- expect(typeof result1.isLatest).toBe('boolean');
157
- expect(typeof result1.satisfiesLatest).toBe('boolean');
158
- expect(typeof result1.enumVersion).toBe('string');
159
- });
160
- });
161
- });
@@ -1,102 +0,0 @@
1
- import { type ILoggerCallback, LogLevel } from '@azure/msal-browser';
2
- import {
3
- type ITelemetryProvider,
4
- TelemetryLevel,
5
- } from '@equinor/fusion-framework-module-telemetry';
6
-
7
- /**
8
- * Maps MSAL log levels to corresponding telemetry levels.
9
- *
10
- * This mapping ensures consistent log level translation between MSAL's logging
11
- * system and the framework's telemetry system.
12
- */
13
- const levelMap: Partial<Record<LogLevel, TelemetryLevel>> = {
14
- [LogLevel.Verbose]: TelemetryLevel.Debug,
15
- [LogLevel.Info]: TelemetryLevel.Information,
16
- [LogLevel.Warning]: TelemetryLevel.Warning,
17
- [LogLevel.Error]: TelemetryLevel.Error,
18
- };
19
-
20
- /**
21
- * Parses MSAL log message into structured components.
22
- *
23
- * MSAL log messages follow the format:
24
- * [timestamp] : [correlationId] : [package] : [level] - [component] - [message]
25
- *
26
- * @param message - Raw MSAL log message
27
- * @returns Parsed message components
28
- */
29
- const parseMsalMessage = (message: string) => {
30
- // Match the structured format: [timestamp] : [correlationId] : [package] : [level] - [component] - [message]
31
- const match = message.match(
32
- /^\[([^\]]+)\]\s*:\s*\[([^\]]*)\]\s*:\s*([^:]+)\s*:\s*(\w+)\s*-\s*([^-]+)\s*-\s*(.*)$/,
33
- );
34
-
35
- // A structured match means we can extract package/component/message parts
36
- if (match) {
37
- const [, _timestamp, _correlationId, packageInfo, _logLevel, component, logMessage] = match;
38
- return {
39
- package: packageInfo.trim() ?? undefined,
40
- component: component.trim() ?? undefined,
41
- message: logMessage.trim() ?? undefined,
42
- };
43
- }
44
-
45
- // Fallback for non-structured messages
46
- return { message: message.trim() };
47
- };
48
-
49
- /**
50
- * Creates a telemetry callback function for MSAL logging integration.
51
- *
52
- * This function bridges MSAL's internal logging system with the framework's
53
- * telemetry infrastructure. It maps MSAL log levels to telemetry levels and
54
- * forwards log events to the provided telemetry provider with structured metadata.
55
- *
56
- * The callback function returned by this method will be called by MSAL whenever
57
- * a log event occurs, allowing for centralized logging and monitoring of
58
- * authentication-related events.
59
- *
60
- * @param provider - Telemetry provider instance to receive log events
61
- * @param metadata - Additional metadata to include with each telemetry event (e.g., module version, environment)
62
- * @param scope - Telemetry scope identifiers for categorization and filtering
63
- * @returns Logger callback function for MSAL that forwards events to telemetry provider
64
- *
65
- * @example
66
- * ```typescript
67
- * const callback = createClientLogCallback(
68
- * telemetryProvider,
69
- * { module: 'msal', version: '4.0.0' },
70
- * ['framework', 'authentication']
71
- * );
72
- *
73
- * // Use with MSAL configuration
74
- * const config = {
75
- * system: {
76
- * loggerOptions: {
77
- * loggerCallback: callback,
78
- * piiLoggingEnabled: false
79
- * }
80
- * }
81
- * };
82
- * ```
83
- */
84
- export const createClientLogCallback = (
85
- provider: ITelemetryProvider,
86
- metadata: Record<string, unknown>,
87
- scope: string[],
88
- ): ILoggerCallback | undefined => {
89
- return (level: LogLevel, message: string, _containsPii: boolean) => {
90
- const parsedMessage = parseMsalMessage(message);
91
-
92
- provider.trackEvent({
93
- name: 'MsalClient',
94
- level: levelMap[level] ?? TelemetryLevel.Information,
95
- scope,
96
- metadata,
97
- properties: {
98
- ...parsedMessage,
99
- },
100
- });
101
- };
102
- };
@@ -1,97 +0,0 @@
1
- import type { IMsalProvider } from './MsalProvider.interface';
2
- import { MsalModuleVersion } from './static';
3
- import { resolveVersion } from './versioning/resolve-version';
4
- import { createProxyProvider as createProxyProvider_v2 } from './v2/create-proxy-provider';
5
- import { createProxyProvider as createProxyProvider_v4 } from './v4/create-proxy-provider';
6
-
7
- /**
8
- * Creates a proxy provider for version compatibility.
9
- *
10
- * This function handles the creation of proxy providers that maintain
11
- * backward compatibility with different MSAL versions while using the
12
- * latest MSAL v4 implementation under the hood.
13
- *
14
- * @param provider - The base MSAL provider instance
15
- * @param version - The target version string (e.g., '2.0.0', '4.0.0')
16
- * @returns A proxy provider compatible with the specified version
17
- *
18
- * @template T - The provider interface type expected by the target version.
19
- * @throws {Error} If the resolved version is not supported.
20
- *
21
- * @example
22
- * ```typescript
23
- * const baseProvider = new MsalProvider(config);
24
- * const v2Proxy = createProxyProvider(baseProvider, '2.0.0');
25
- * ```
26
- */
27
- export function createProxyProvider<T = IMsalProvider>(
28
- provider: IMsalProvider,
29
- version: string,
30
- ): T {
31
- // Resolve the requested version to determine which proxy to create
32
- const { enumVersion } = resolveVersion(version);
33
-
34
- // Build the version-appropriate proxy based on the resolved MSAL module version
35
- switch (enumVersion) {
36
- case MsalModuleVersion.V2:
37
- // Create v2-compatible proxy with legacy API adapters
38
- return createProxyProvider_v2(provider) as T;
39
- case MsalModuleVersion.V4:
40
- // Create v4-compatible proxy (passthrough with v4 version metadata)
41
- return createProxyProvider_v4(provider) as T;
42
- case MsalModuleVersion.V5:
43
- // Create transparent proxy for v5 - passes through to original provider
44
- return new Proxy(provider, {
45
- get: (target: IMsalProvider, prop: keyof IMsalProvider) => {
46
- // Delegate every known property/method to the underlying provider
47
- switch (prop) {
48
- case 'version': {
49
- return target.version;
50
- }
51
- case 'msalVersion': {
52
- return enumVersion;
53
- }
54
- case 'client': {
55
- return target.client;
56
- }
57
- case 'account': {
58
- return target.account;
59
- }
60
- case 'acquireAccessToken': {
61
- return target.acquireAccessToken.bind(target);
62
- }
63
- case 'acquireToken': {
64
- return target.acquireToken.bind(target);
65
- }
66
- case 'login': {
67
- return target.login.bind(target);
68
- }
69
- case 'logout': {
70
- return target.logout.bind(target);
71
- }
72
- case 'handleRedirect': {
73
- return target.handleRedirect.bind(target);
74
- }
75
- case 'createProxyProvider': {
76
- return target.createProxyProvider.bind(target);
77
- }
78
- case 'initialize': {
79
- return () => {
80
- // noop - initialize is handled by the provider, not the proxy
81
- };
82
- }
83
- default: {
84
- // backstop to prevent then from being called - this is not an async operation
85
- if (prop === 'then') return undefined;
86
-
87
- // Exhaustive check: TypeScript-only guard to ensure all IMsalProvider keys are handled
88
- const exhausted: never = prop;
89
- return (target as IMsalProvider)[exhausted];
90
- }
91
- }
92
- },
93
- }) as T;
94
- default:
95
- throw new Error(`Version ${version} is not supported`);
96
- }
97
- }
package/src/index.ts DELETED
@@ -1,48 +0,0 @@
1
- /**
2
- * @packageDocumentation
3
- *
4
- * MSAL authentication module for Fusion Framework.
5
- *
6
- * Provides Microsoft Authentication Library (MSAL) integration with support for:
7
- * - Azure AD / Entra ID authentication (SSO, popup, redirect)
8
- * - Automatic token management with silent refresh
9
- * - Module hoisting for shared auth state across application scopes
10
- * - Backward-compatible v2 proxy layer alongside native MSAL v4/v5 API
11
- * - Backend-issued SPA authorization code exchange
12
- *
13
- * @example
14
- * ```typescript
15
- * import { enableMSAL } from '@equinor/fusion-framework-module-msal';
16
- *
17
- * enableMSAL(configurator, (builder) => {
18
- * builder.setClientConfig({
19
- * auth: { clientId: 'your-client-id', tenantId: 'your-tenant-id' },
20
- * });
21
- * builder.setRequiresAuth(true);
22
- * });
23
- * ```
24
- *
25
- * @module @equinor/fusion-framework-module-msal
26
- */
27
-
28
- export {
29
- module,
30
- configureMsal,
31
- enableMSAL,
32
- type MsalModule,
33
- type AuthConfigFn,
34
- } from './module';
35
-
36
- export type { IMsalProvider } from './MsalProvider.interface';
37
- export type { IMsalClient } from './MsalClient.interface';
38
- export { MsalClient, type MsalClientConfig } from './MsalClient';
39
-
40
- /**
41
- * Required to implement {@link IMsalProvider}, whose `msalVersion` member is
42
- * typed as this enum.
43
- */
44
- export { MsalModuleVersion } from './static';
45
-
46
- export type { AccountInfo, AuthenticationResult } from './types';
47
-
48
- export { default } from './module';