@equinor/fusion-framework-module-msal 7.2.2 → 7.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/README.md +10 -1
- package/dist/esm/MsalConfigurator.js +7 -2
- package/dist/esm/MsalConfigurator.js.map +1 -1
- package/dist/esm/MsalProvider.js +1 -1
- package/dist/esm/MsalProvider.js.map +1 -1
- package/dist/esm/__tests__/MsalConfigurator.test.js +39 -0
- package/dist/esm/__tests__/MsalConfigurator.test.js.map +1 -0
- package/dist/esm/__tests__/MsalProvider.test.js +53 -0
- package/dist/esm/__tests__/MsalProvider.test.js.map +1 -0
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/MsalConfigurator.d.ts +6 -2
- package/dist/types/__tests__/MsalConfigurator.test.d.ts +1 -0
- package/dist/types/__tests__/MsalProvider.test.d.ts +1 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +3 -3
- package/src/MsalConfigurator.ts +8 -3
- package/src/MsalProvider.ts +1 -1
- package/src/__tests__/MsalConfigurator.test.ts +64 -0
- package/src/__tests__/MsalProvider.test.ts +74 -0
- package/src/version.ts +1 -1
|
@@ -76,7 +76,7 @@ export declare class MsalConfigurator extends BaseConfigBuilder<MsalConfig> {
|
|
|
76
76
|
* This follows Microsoft's standard SPA Auth Code Flow pattern and is compatible with
|
|
77
77
|
* MSAL Browser's acquireTokenByCode() method.
|
|
78
78
|
*
|
|
79
|
-
* @param authCode - The authorization code issued by the backend
|
|
79
|
+
* @param authCode - The authorization code issued by the backend, or undefined to clear/reset it
|
|
80
80
|
* @returns The configurator instance for method chaining
|
|
81
81
|
*
|
|
82
82
|
* @example
|
|
@@ -84,15 +84,19 @@ export declare class MsalConfigurator extends BaseConfigBuilder<MsalConfig> {
|
|
|
84
84
|
* // Backend provides auth code in HTML/config
|
|
85
85
|
* const config = { auth: { code: getAuthCodeFromBackend() } };
|
|
86
86
|
* configurator.setAuthCode(config.auth.code);
|
|
87
|
+
*
|
|
88
|
+
* // Clear previously configured auth code
|
|
89
|
+
* configurator.setAuthCode(undefined);
|
|
87
90
|
* ```
|
|
88
91
|
*
|
|
89
92
|
* @remarks
|
|
90
93
|
* - Auth codes are single-use and short-lived (typically 5-10 minutes)
|
|
91
94
|
* - The exchange happens during module initialization before requiresAuth check
|
|
92
95
|
* - If exchange fails, the provider falls back to standard MSAL authentication flows
|
|
96
|
+
* - Passing undefined, empty, or whitespace-only values clears the configured auth code
|
|
93
97
|
* - Requires backend to be configured with SPA Auth Code support
|
|
94
98
|
*/
|
|
95
|
-
setAuthCode(authCode
|
|
99
|
+
setAuthCode(authCode?: string): this;
|
|
96
100
|
/**
|
|
97
101
|
* Sets whether authentication is required for the application.
|
|
98
102
|
*
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "7.
|
|
1
|
+
export declare const version = "7.3.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@equinor/fusion-framework-module-msal",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.3.0",
|
|
4
4
|
"description": "Microsoft Authentication Library (MSAL) integration module for Fusion Framework",
|
|
5
5
|
"main": "dist/esm/index.js",
|
|
6
6
|
"types": "dist/types/index.d.ts",
|
|
@@ -58,8 +58,8 @@
|
|
|
58
58
|
"semver": "^7.5.4",
|
|
59
59
|
"typescript": "^5.8.2",
|
|
60
60
|
"zod": "^4.1.8",
|
|
61
|
-
"@equinor/fusion-framework-module
|
|
62
|
-
"@equinor/fusion-framework-module": "^
|
|
61
|
+
"@equinor/fusion-framework-module": "^5.0.5",
|
|
62
|
+
"@equinor/fusion-framework-module-telemetry": "^4.6.3"
|
|
63
63
|
},
|
|
64
64
|
"peerDependenciesMeta": {
|
|
65
65
|
"@equinor/fusion-framework-module-telemetry": {
|
package/src/MsalConfigurator.ts
CHANGED
|
@@ -136,7 +136,7 @@ export class MsalConfigurator extends BaseConfigBuilder<MsalConfig> {
|
|
|
136
136
|
* This follows Microsoft's standard SPA Auth Code Flow pattern and is compatible with
|
|
137
137
|
* MSAL Browser's acquireTokenByCode() method.
|
|
138
138
|
*
|
|
139
|
-
* @param authCode - The authorization code issued by the backend
|
|
139
|
+
* @param authCode - The authorization code issued by the backend, or undefined to clear/reset it
|
|
140
140
|
* @returns The configurator instance for method chaining
|
|
141
141
|
*
|
|
142
142
|
* @example
|
|
@@ -144,16 +144,21 @@ export class MsalConfigurator extends BaseConfigBuilder<MsalConfig> {
|
|
|
144
144
|
* // Backend provides auth code in HTML/config
|
|
145
145
|
* const config = { auth: { code: getAuthCodeFromBackend() } };
|
|
146
146
|
* configurator.setAuthCode(config.auth.code);
|
|
147
|
+
*
|
|
148
|
+
* // Clear previously configured auth code
|
|
149
|
+
* configurator.setAuthCode(undefined);
|
|
147
150
|
* ```
|
|
148
151
|
*
|
|
149
152
|
* @remarks
|
|
150
153
|
* - Auth codes are single-use and short-lived (typically 5-10 minutes)
|
|
151
154
|
* - The exchange happens during module initialization before requiresAuth check
|
|
152
155
|
* - If exchange fails, the provider falls back to standard MSAL authentication flows
|
|
156
|
+
* - Passing undefined, empty, or whitespace-only values clears the configured auth code
|
|
153
157
|
* - Requires backend to be configured with SPA Auth Code support
|
|
154
158
|
*/
|
|
155
|
-
setAuthCode(authCode
|
|
156
|
-
|
|
159
|
+
setAuthCode(authCode?: string): this {
|
|
160
|
+
const normalizedAuthCode = authCode?.trim() || undefined;
|
|
161
|
+
this._set('authCode', async () => normalizedAuthCode);
|
|
157
162
|
return this;
|
|
158
163
|
}
|
|
159
164
|
|
package/src/MsalProvider.ts
CHANGED
|
@@ -142,7 +142,7 @@ export class MsalProvider extends BaseModuleProvider<MsalConfig> implements IMsa
|
|
|
142
142
|
|
|
143
143
|
// Extract auth code from config if present
|
|
144
144
|
// This will be used during initialize to exchange for tokens
|
|
145
|
-
this.#authCode = config.authCode;
|
|
145
|
+
this.#authCode = config.authCode?.trim() || undefined;
|
|
146
146
|
|
|
147
147
|
// Validate required client configuration
|
|
148
148
|
if (!config.client) {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { ConfigBuilderCallbackArgs } from '@equinor/fusion-framework-module';
|
|
2
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
3
|
+
import type { IMsalClient } from '../MsalClient.interface';
|
|
4
|
+
import { type MsalConfig, MsalConfigurator } from '../MsalConfigurator';
|
|
5
|
+
|
|
6
|
+
const createConfigCallbackArgs = (): ConfigBuilderCallbackArgs => ({
|
|
7
|
+
config: {},
|
|
8
|
+
hasModule: vi.fn().mockReturnValue(false),
|
|
9
|
+
requireInstance: vi.fn(),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
const createClient = (): IMsalClient => ({}) as IMsalClient;
|
|
13
|
+
|
|
14
|
+
const createInitialConfig = (): Pick<MsalConfig, 'telemetry'> => ({
|
|
15
|
+
telemetry: {
|
|
16
|
+
metadata: {},
|
|
17
|
+
scope: [],
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
describe('MsalConfigurator', () => {
|
|
22
|
+
it('setAuthCode should normalize surrounding whitespace', async () => {
|
|
23
|
+
const configurator = new MsalConfigurator();
|
|
24
|
+
|
|
25
|
+
configurator.setClient(createClient());
|
|
26
|
+
configurator.setAuthCode(' auth-code ');
|
|
27
|
+
|
|
28
|
+
const config = await configurator.createConfigAsync(
|
|
29
|
+
createConfigCallbackArgs(),
|
|
30
|
+
createInitialConfig(),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
expect(config.authCode).toBe('auth-code');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('setAuthCode should allow clearing with undefined', async () => {
|
|
37
|
+
const configurator = new MsalConfigurator();
|
|
38
|
+
|
|
39
|
+
configurator.setClient(createClient());
|
|
40
|
+
configurator.setAuthCode('auth-code');
|
|
41
|
+
configurator.setAuthCode(undefined);
|
|
42
|
+
|
|
43
|
+
const config = await configurator.createConfigAsync(
|
|
44
|
+
createConfigCallbackArgs(),
|
|
45
|
+
createInitialConfig(),
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
expect(config.authCode).toBeUndefined();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('setAuthCode should treat whitespace-only values as undefined', async () => {
|
|
52
|
+
const configurator = new MsalConfigurator();
|
|
53
|
+
|
|
54
|
+
configurator.setClient(createClient());
|
|
55
|
+
configurator.setAuthCode(' ');
|
|
56
|
+
|
|
57
|
+
const config = await configurator.createConfigAsync(
|
|
58
|
+
createConfigCallbackArgs(),
|
|
59
|
+
createInitialConfig(),
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
expect(config.authCode).toBeUndefined();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import type { IMsalClient } from '../MsalClient.interface';
|
|
3
|
+
import type { MsalConfig } from '../MsalConfigurator';
|
|
4
|
+
import { MsalProvider } from '../MsalProvider';
|
|
5
|
+
import type { AuthenticationResult } from '../types';
|
|
6
|
+
|
|
7
|
+
type MockMsalClient = {
|
|
8
|
+
client: IMsalClient;
|
|
9
|
+
acquireTokenByCode: ReturnType<typeof vi.fn>;
|
|
10
|
+
initialize: ReturnType<typeof vi.fn>;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const createClient = (): MockMsalClient => {
|
|
14
|
+
const initialize = vi.fn(async () => undefined);
|
|
15
|
+
const acquireTokenByCode = vi.fn(async () => ({}) as AuthenticationResult);
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
client: {
|
|
19
|
+
clientId: 'test-client-id',
|
|
20
|
+
initialize,
|
|
21
|
+
acquireTokenByCode,
|
|
22
|
+
setActiveAccount: vi.fn(),
|
|
23
|
+
} as unknown as IMsalClient,
|
|
24
|
+
acquireTokenByCode,
|
|
25
|
+
initialize,
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
const createConfig = (client: IMsalClient, authCode?: string): MsalConfig => ({
|
|
30
|
+
client,
|
|
31
|
+
version: '7.0.0',
|
|
32
|
+
requiresAuth: false,
|
|
33
|
+
authCode,
|
|
34
|
+
telemetry: {
|
|
35
|
+
metadata: { module: 'msal', version: '7.0.0' },
|
|
36
|
+
scope: ['framework', 'authentication'],
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('MsalProvider.initialize', () => {
|
|
41
|
+
it('should not attempt auth code exchange when auth code is undefined', async () => {
|
|
42
|
+
const mockClient = createClient();
|
|
43
|
+
|
|
44
|
+
const provider = new MsalProvider(createConfig(mockClient.client, undefined));
|
|
45
|
+
await provider.initialize();
|
|
46
|
+
|
|
47
|
+
expect(mockClient.initialize).toHaveBeenCalledTimes(1);
|
|
48
|
+
expect(mockClient.acquireTokenByCode).not.toHaveBeenCalled();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should not attempt auth code exchange when auth code is whitespace-only', async () => {
|
|
52
|
+
const mockClient = createClient();
|
|
53
|
+
|
|
54
|
+
const provider = new MsalProvider(createConfig(mockClient.client, ' '));
|
|
55
|
+
await provider.initialize();
|
|
56
|
+
|
|
57
|
+
expect(mockClient.acquireTokenByCode).not.toHaveBeenCalled();
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('should exchange auth code once and clear it afterwards', async () => {
|
|
61
|
+
const mockClient = createClient();
|
|
62
|
+
|
|
63
|
+
const provider = new MsalProvider(createConfig(mockClient.client, 'auth-code'));
|
|
64
|
+
|
|
65
|
+
await provider.initialize();
|
|
66
|
+
await provider.initialize();
|
|
67
|
+
|
|
68
|
+
expect(mockClient.acquireTokenByCode).toHaveBeenCalledTimes(1);
|
|
69
|
+
expect(mockClient.acquireTokenByCode).toHaveBeenCalledWith({
|
|
70
|
+
code: 'auth-code',
|
|
71
|
+
scopes: ['test-client-id/.default'],
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
});
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '7.
|
|
2
|
+
export const version = '7.3.0';
|