@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.
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/version.d.ts +1 -1
- package/package.json +8 -5
- package/CHANGELOG.md +0 -1212
- package/docs/api-reference.md +0 -85
- package/docs/auth-code-flow.md +0 -86
- package/docs/migration-v2-to-v4.md +0 -115
- package/docs/testing.md +0 -191
- package/docs/troubleshooting.md +0 -17
- package/docs/version-management.md +0 -67
- package/src/MsalClient.interface.ts +0 -139
- package/src/MsalClient.ts +0 -326
- package/src/MsalConfigurator.ts +0 -486
- package/src/MsalProvider.interface.ts +0 -179
- package/src/MsalProvider.ts +0 -776
- package/src/MsalProxyProvider.interface.ts +0 -72
- package/src/__tests__/MsalConfigurator.test.ts +0 -222
- package/src/__tests__/MsalProvider.test.ts +0 -74
- package/src/__tests__/create-proxy-provider.test.ts +0 -77
- package/src/__tests__/mock/create-mock-user-from-token.test.ts +0 -46
- package/src/__tests__/mock/msal-mock.test.ts +0 -613
- package/src/__tests__/versioning/resolve-version.test.ts +0 -161
- package/src/create-client-log-callback.ts +0 -102
- package/src/create-proxy-provider.ts +0 -97
- package/src/index.ts +0 -48
- package/src/mock/MsalMockClient.ts +0 -618
- package/src/mock/MsalMockConfigurator.ts +0 -305
- package/src/mock/create-mock-token.ts +0 -92
- package/src/mock/create-mock-user-from-token.ts +0 -46
- package/src/mock/create-msal-mock-client.ts +0 -25
- package/src/mock/decode-jwt-segment.ts +0 -22
- package/src/mock/index.ts +0 -30
- package/src/mock/module.ts +0 -54
- package/src/module.ts +0 -142
- package/src/msal-config-schema.ts +0 -81
- package/src/static.ts +0 -38
- package/src/telemetry-config-schema.ts +0 -25
- package/src/types.ts +0 -16
- package/src/util/compare-origin.ts +0 -18
- package/src/util/normalize-uri.ts +0 -24
- package/src/util/redirect.ts +0 -19
- package/src/v2/IAuthClient.interface.ts +0 -114
- package/src/v2/Logger.ts +0 -204
- package/src/v2/MsalProvider.interface.ts +0 -102
- package/src/v2/create-proxy-client.ts +0 -195
- package/src/v2/create-proxy-provider.ts +0 -177
- package/src/v2/map-account-info.ts +0 -23
- package/src/v2/map-authentication-result.ts +0 -28
- package/src/v2/types.ts +0 -674
- package/src/v4/create-proxy-provider.ts +0 -75
- package/src/v4/index.ts +0 -13
- package/src/v4/types.ts +0 -727
- package/src/version.ts +0 -2
- package/src/versioning/VersionError.ts +0 -64
- package/src/versioning/index.ts +0 -29
- package/src/versioning/resolve-version.ts +0 -154
- package/src/versioning/types.ts +0 -60
- package/tsconfig.json +0 -18
- package/vitest.config.ts +0 -11
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import type { SemVer } from 'semver';
|
|
2
|
-
import type { MsalModuleVersion } from './static';
|
|
3
|
-
|
|
4
|
-
import type { IMsalProvider } from './MsalProvider.interface';
|
|
5
|
-
import type { IMsalProvider as IMsalProvider_v2 } from './v2/MsalProvider.interface';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Type mapping between MSAL module versions and their corresponding provider interfaces.
|
|
9
|
-
*
|
|
10
|
-
* This mapping ensures type-safe creation of proxy providers for different MSAL versions.
|
|
11
|
-
* Each version maps to its appropriate provider interface type.
|
|
12
|
-
*
|
|
13
|
-
* @internal
|
|
14
|
-
*/
|
|
15
|
-
type ProxyProviderMap = {
|
|
16
|
-
[MsalModuleVersion.V2]: IMsalProvider_v2;
|
|
17
|
-
[MsalModuleVersion.V4]: IMsalProvider;
|
|
18
|
-
[MsalModuleVersion.V5]: IMsalProvider;
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Interface for providers that can create version-compatible proxy providers.
|
|
23
|
-
*
|
|
24
|
-
* This interface enables backward compatibility by allowing providers to create
|
|
25
|
-
* proxies that adapt their API to match different MSAL version signatures. The proxy
|
|
26
|
-
* wraps the v4 implementation and exposes it through older version interfaces.
|
|
27
|
-
*
|
|
28
|
-
* @remarks
|
|
29
|
-
* This interface should ideally be defined in the @equinor/fusion-framework-module package
|
|
30
|
-
* for broader framework compatibility.
|
|
31
|
-
*
|
|
32
|
-
* @property version - The semantic version of the provider
|
|
33
|
-
* @property msalVersion - The MSAL module version enum value
|
|
34
|
-
* @property createProxyProvider - Method to create a version-specific proxy provider
|
|
35
|
-
*
|
|
36
|
-
* @example
|
|
37
|
-
* ```typescript
|
|
38
|
-
* const provider: IMsalProvider = new MsalProvider(config);
|
|
39
|
-
*
|
|
40
|
-
* // Create a v2-compatible proxy
|
|
41
|
-
* const v2Proxy = provider.createProxyProvider('2.0.0');
|
|
42
|
-
* // v2Proxy now has v2-compatible method signatures
|
|
43
|
-
* ```
|
|
44
|
-
*/
|
|
45
|
-
export interface IProxyProvider {
|
|
46
|
-
/**
|
|
47
|
-
* The semantic version of the provider.
|
|
48
|
-
*
|
|
49
|
-
* This represents the actual version number of the MSAL implementation,
|
|
50
|
-
* following semantic versioning (semver) standards.
|
|
51
|
-
*/
|
|
52
|
-
readonly version: string | SemVer;
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* The MSAL module version enum value indicating the API compatibility level.
|
|
56
|
-
*
|
|
57
|
-
* This property specifies which MSAL version's API surface this provider implements,
|
|
58
|
-
* allowing for version-specific behavior and proxy provider creation.
|
|
59
|
-
*/
|
|
60
|
-
msalVersion: MsalModuleVersion;
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Creates a proxy provider compatible with the specified MSAL version.
|
|
64
|
-
*
|
|
65
|
-
* The proxy adapts the provider's v4 API to match the requested version's interface,
|
|
66
|
-
* enabling backward compatibility during migration scenarios.
|
|
67
|
-
*
|
|
68
|
-
* @param version - Target version key (V2, V4, or Latest)
|
|
69
|
-
* @returns Proxy provider with version-specific type
|
|
70
|
-
*/
|
|
71
|
-
createProxyProvider<T extends keyof ProxyProviderMap>(version: T): ProxyProviderMap[T];
|
|
72
|
-
}
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import type { ConfigBuilderCallbackArgs } from '@equinor/fusion-framework-module';
|
|
2
|
-
import { CacheLookupPolicy } from '@azure/msal-browser';
|
|
3
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
4
|
-
import type { IMsalClient } from '../MsalClient.interface';
|
|
5
|
-
import { type MsalConfig, MsalConfigurator } from '../MsalConfigurator';
|
|
6
|
-
|
|
7
|
-
const createConfigCallbackArgs = (): ConfigBuilderCallbackArgs => ({
|
|
8
|
-
config: {},
|
|
9
|
-
hasModule: vi.fn().mockReturnValue(false),
|
|
10
|
-
requireInstance: vi.fn(),
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
const createClient = (): IMsalClient => ({}) as IMsalClient;
|
|
14
|
-
|
|
15
|
-
const createInitialConfig = (): Pick<MsalConfig, 'telemetry'> => ({
|
|
16
|
-
telemetry: {
|
|
17
|
-
metadata: {},
|
|
18
|
-
scope: [],
|
|
19
|
-
},
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
describe('MsalConfigurator', () => {
|
|
23
|
-
it('enriches a copy, leaving the declared client configuration untouched', async () => {
|
|
24
|
-
// A caller may reuse or assert on the object it passed, and the defaults
|
|
25
|
-
// applied here are derived — rewriting it behind their back is not ours to do
|
|
26
|
-
const declared = { auth: { clientId: 'my-app', tenantId: 'my-tenant' } };
|
|
27
|
-
const configurator = new MsalConfigurator();
|
|
28
|
-
|
|
29
|
-
configurator.setClientConfig(declared);
|
|
30
|
-
|
|
31
|
-
const config = await configurator.createConfigAsync(
|
|
32
|
-
createConfigCallbackArgs(),
|
|
33
|
-
createInitialConfig(),
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
expect(declared).toEqual({ auth: { clientId: 'my-app', tenantId: 'my-tenant' } });
|
|
37
|
-
expect(config.client?.tenantId).toBe('my-tenant');
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
it('setAuthCode should normalize surrounding whitespace', async () => {
|
|
41
|
-
const configurator = new MsalConfigurator();
|
|
42
|
-
|
|
43
|
-
configurator.setClient(createClient());
|
|
44
|
-
configurator.setAuthCode(' auth-code ');
|
|
45
|
-
|
|
46
|
-
const config = await configurator.createConfigAsync(
|
|
47
|
-
createConfigCallbackArgs(),
|
|
48
|
-
createInitialConfig(),
|
|
49
|
-
);
|
|
50
|
-
|
|
51
|
-
expect(config.authCode).toBe('auth-code');
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
it('setAuthCode should allow clearing with undefined', async () => {
|
|
55
|
-
const configurator = new MsalConfigurator();
|
|
56
|
-
|
|
57
|
-
configurator.setClient(createClient());
|
|
58
|
-
configurator.setAuthCode('auth-code');
|
|
59
|
-
configurator.setAuthCode(undefined);
|
|
60
|
-
|
|
61
|
-
const config = await configurator.createConfigAsync(
|
|
62
|
-
createConfigCallbackArgs(),
|
|
63
|
-
createInitialConfig(),
|
|
64
|
-
);
|
|
65
|
-
|
|
66
|
-
expect(config.authCode).toBeUndefined();
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('setAuthCode should treat whitespace-only values as undefined', async () => {
|
|
70
|
-
const configurator = new MsalConfigurator();
|
|
71
|
-
|
|
72
|
-
configurator.setClient(createClient());
|
|
73
|
-
configurator.setAuthCode(' ');
|
|
74
|
-
|
|
75
|
-
const config = await configurator.createConfigAsync(
|
|
76
|
-
createConfigCallbackArgs(),
|
|
77
|
-
createInitialConfig(),
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
expect(config.authCode).toBeUndefined();
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it('createConfigAsync should succeed when client is omitted', async () => {
|
|
84
|
-
const configurator = new MsalConfigurator();
|
|
85
|
-
|
|
86
|
-
const config = await configurator.createConfigAsync(
|
|
87
|
-
createConfigCallbackArgs(),
|
|
88
|
-
createInitialConfig(),
|
|
89
|
-
);
|
|
90
|
-
|
|
91
|
-
expect(config.client).toBeUndefined();
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
describe('_createClient', () => {
|
|
95
|
-
it('builds from the same resolved client config the real client would get', async () => {
|
|
96
|
-
// The mock relies on this: substituting the client must not also mean
|
|
97
|
-
// re-implementing authority, cache and telemetry resolution
|
|
98
|
-
const received: unknown[] = [];
|
|
99
|
-
class CustomConfigurator extends MsalConfigurator {
|
|
100
|
-
protected override async _createClient(config: MsalConfig): Promise<IMsalClient> {
|
|
101
|
-
received.push(this._createClientConfig(config));
|
|
102
|
-
return createClient();
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const configurator = new CustomConfigurator();
|
|
107
|
-
configurator.setClientConfig({ auth: { clientId: 'client-id', tenantId: 'tenant-id' } });
|
|
108
|
-
|
|
109
|
-
await configurator.createConfigAsync(createConfigCallbackArgs(), createInitialConfig());
|
|
110
|
-
|
|
111
|
-
expect(received).toEqual([
|
|
112
|
-
expect.objectContaining({
|
|
113
|
-
auth: expect.objectContaining({
|
|
114
|
-
clientId: 'client-id',
|
|
115
|
-
// derived by the configurator, not by the caller
|
|
116
|
-
authority: 'https://login.microsoftonline.com/tenant-id',
|
|
117
|
-
}),
|
|
118
|
-
cache: { cacheLocation: 'localStorage' },
|
|
119
|
-
}),
|
|
120
|
-
]);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
it('supplies the client when none was set', async () => {
|
|
124
|
-
const client = createClient();
|
|
125
|
-
class CustomConfigurator extends MsalConfigurator {
|
|
126
|
-
protected override async _createClient(): Promise<IMsalClient> {
|
|
127
|
-
return client;
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const config = await new CustomConfigurator().createConfigAsync(
|
|
132
|
-
createConfigCallbackArgs(),
|
|
133
|
-
createInitialConfig(),
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
expect(config.client).toBe(client);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
it('is not consulted when a client was set, so setClient always wins', async () => {
|
|
140
|
-
const own = createClient();
|
|
141
|
-
const createOther = vi.fn().mockResolvedValue(createClient());
|
|
142
|
-
class CustomConfigurator extends MsalConfigurator {
|
|
143
|
-
protected override _createClient(): Promise<IMsalClient> {
|
|
144
|
-
return createOther();
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const configurator = new CustomConfigurator();
|
|
149
|
-
configurator.setClient(own);
|
|
150
|
-
|
|
151
|
-
const config = await configurator.createConfigAsync(
|
|
152
|
-
createConfigCallbackArgs(),
|
|
153
|
-
createInitialConfig(),
|
|
154
|
-
);
|
|
155
|
-
|
|
156
|
-
expect(config.client).toBe(own);
|
|
157
|
-
expect(createOther).not.toHaveBeenCalled();
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
it('is not consulted when hoisted, so a host provider is never shadowed', async () => {
|
|
161
|
-
// A hoisted module authenticates through the host's provider, so anything
|
|
162
|
-
// built here would be discarded — or worse, shadow the host's user
|
|
163
|
-
const createOther = vi.fn().mockResolvedValue(createClient());
|
|
164
|
-
class CustomConfigurator extends MsalConfigurator {
|
|
165
|
-
protected override _createClient(): Promise<IMsalClient> {
|
|
166
|
-
return createOther();
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
const configurator = new CustomConfigurator();
|
|
171
|
-
configurator.setClientConfig({ auth: { clientId: 'client-id', tenantId: 'tenant-id' } });
|
|
172
|
-
|
|
173
|
-
const config = await configurator.createConfigAsync(
|
|
174
|
-
{ ...createConfigCallbackArgs(), ref: { auth: {} } },
|
|
175
|
-
createInitialConfig(),
|
|
176
|
-
);
|
|
177
|
-
|
|
178
|
-
expect(config.client).toBeUndefined();
|
|
179
|
-
expect(createOther).not.toHaveBeenCalled();
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
describe('cacheLookupPolicy', () => {
|
|
184
|
-
it('defaults to CacheLookupPolicy.AccessTokenAndRefreshToken', async () => {
|
|
185
|
-
const configurator = new MsalConfigurator();
|
|
186
|
-
configurator.setClient(createClient());
|
|
187
|
-
|
|
188
|
-
const config = await configurator.createConfigAsync(
|
|
189
|
-
createConfigCallbackArgs(),
|
|
190
|
-
createInitialConfig(),
|
|
191
|
-
);
|
|
192
|
-
|
|
193
|
-
expect(config.cacheLookupPolicy).toBe(CacheLookupPolicy.AccessTokenAndRefreshToken);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it('setCacheLookupPolicy(undefined) clears the policy so MSAL default applies', async () => {
|
|
197
|
-
const configurator = new MsalConfigurator();
|
|
198
|
-
configurator.setClient(createClient());
|
|
199
|
-
configurator.setCacheLookupPolicy(undefined);
|
|
200
|
-
|
|
201
|
-
const config = await configurator.createConfigAsync(
|
|
202
|
-
createConfigCallbackArgs(),
|
|
203
|
-
createInitialConfig(),
|
|
204
|
-
);
|
|
205
|
-
|
|
206
|
-
expect(config.cacheLookupPolicy).toBeUndefined();
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
it('setCacheLookupPolicy overrides the default', async () => {
|
|
210
|
-
const configurator = new MsalConfigurator();
|
|
211
|
-
configurator.setClient(createClient());
|
|
212
|
-
configurator.setCacheLookupPolicy(CacheLookupPolicy.Default);
|
|
213
|
-
|
|
214
|
-
const config = await configurator.createConfigAsync(
|
|
215
|
-
createConfigCallbackArgs(),
|
|
216
|
-
createInitialConfig(),
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
expect(config.cacheLookupPolicy).toBe(CacheLookupPolicy.Default);
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
});
|
|
@@ -1,74 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,77 +0,0 @@
|
|
|
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 { MsalModuleVersion } from '../static';
|
|
6
|
-
import type { AuthenticationResult } from '../types';
|
|
7
|
-
import type { IMsalProvider as IMsalProvider_v2 } from '../v2/MsalProvider.interface';
|
|
8
|
-
|
|
9
|
-
type MockMsalClient = {
|
|
10
|
-
client: IMsalClient;
|
|
11
|
-
acquireToken: ReturnType<typeof vi.fn>;
|
|
12
|
-
handleRedirectPromise: ReturnType<typeof vi.fn>;
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
const createClient = (): MockMsalClient => {
|
|
16
|
-
const acquireToken = vi.fn(async () => ({ accessToken: 'v4-token' }) as AuthenticationResult);
|
|
17
|
-
const handleRedirectPromise = vi.fn(
|
|
18
|
-
async () => ({ accessToken: 'redirect-token' }) as AuthenticationResult,
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
return {
|
|
22
|
-
client: {
|
|
23
|
-
clientId: 'test-client-id',
|
|
24
|
-
initialize: vi.fn(async () => undefined),
|
|
25
|
-
acquireToken,
|
|
26
|
-
handleRedirectPromise,
|
|
27
|
-
setActiveAccount: vi.fn(),
|
|
28
|
-
getActiveAccount: vi.fn(() => null),
|
|
29
|
-
} as unknown as IMsalClient,
|
|
30
|
-
acquireToken,
|
|
31
|
-
handleRedirectPromise,
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const createConfig = (client: IMsalClient): MsalConfig => ({
|
|
36
|
-
client,
|
|
37
|
-
version: '7.0.0',
|
|
38
|
-
requiresAuth: false,
|
|
39
|
-
telemetry: {
|
|
40
|
-
metadata: { module: 'msal', version: '7.0.0' },
|
|
41
|
-
scope: ['framework', 'authentication'],
|
|
42
|
-
},
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
describe('MsalProvider.createProxyProvider (v2)', () => {
|
|
46
|
-
it('tags the proxy as v2 while delegating through the same v4 provider', () => {
|
|
47
|
-
const mockClient = createClient();
|
|
48
|
-
const provider = new MsalProvider(createConfig(mockClient.client));
|
|
49
|
-
|
|
50
|
-
const v2Provider = provider.createProxyProvider<IMsalProvider_v2>(MsalModuleVersion.V2);
|
|
51
|
-
|
|
52
|
-
expect(v2Provider.msalVersion).toBe(MsalModuleVersion.V2);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('adapts the legacy v2 acquireToken shape into a v4 request before delegating', async () => {
|
|
56
|
-
const mockClient = createClient();
|
|
57
|
-
const provider = new MsalProvider(createConfig(mockClient.client));
|
|
58
|
-
const v2Provider = provider.createProxyProvider<IMsalProvider_v2>(MsalModuleVersion.V2);
|
|
59
|
-
|
|
60
|
-
await expect(v2Provider.acquireToken({ scopes: ['User.Read'] })).resolves.toMatchObject({
|
|
61
|
-
accessToken: 'v4-token',
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
expect(mockClient.acquireToken).toHaveBeenCalledWith(
|
|
65
|
-
expect.objectContaining({ request: expect.objectContaining({ scopes: ['User.Read'] }) }),
|
|
66
|
-
);
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it('discards the host’s redirect result, honoring v2’s null contract, while still processing it', async () => {
|
|
70
|
-
const mockClient = createClient();
|
|
71
|
-
const provider = new MsalProvider(createConfig(mockClient.client));
|
|
72
|
-
const v2Provider = provider.createProxyProvider<IMsalProvider_v2>(MsalModuleVersion.V2);
|
|
73
|
-
|
|
74
|
-
await expect(v2Provider.handleRedirect()).resolves.toBeNull();
|
|
75
|
-
expect(mockClient.handleRedirectPromise).toHaveBeenCalledTimes(1);
|
|
76
|
-
});
|
|
77
|
-
});
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest';
|
|
2
|
-
|
|
3
|
-
import { createMockToken } from '../../mock/create-mock-token';
|
|
4
|
-
import { createMockUserFromToken } from '../../mock/create-mock-user-from-token';
|
|
5
|
-
|
|
6
|
-
describe('createMockUserFromToken', () => {
|
|
7
|
-
it('maps identity claims onto the matching MsalMockUser fields', () => {
|
|
8
|
-
const token = createMockToken({
|
|
9
|
-
name: 'Ada Lovelace',
|
|
10
|
-
preferred_username: 'ada@equinor.com',
|
|
11
|
-
oid: 'ada-object-id',
|
|
12
|
-
tid: 'ada-tenant-id',
|
|
13
|
-
scp: 'User.Read Files.Read',
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
expect(createMockUserFromToken(token)).toEqual({
|
|
17
|
-
name: 'Ada Lovelace',
|
|
18
|
-
username: 'ada@equinor.com',
|
|
19
|
-
userId: 'ada-object-id',
|
|
20
|
-
tenantId: 'ada-tenant-id',
|
|
21
|
-
scopes: ['User.Read', 'Files.Read'],
|
|
22
|
-
});
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
it('leaves a field undefined rather than fabricating one, when a claim is absent', () => {
|
|
26
|
-
const token = createMockToken({
|
|
27
|
-
name: undefined,
|
|
28
|
-
preferred_username: undefined,
|
|
29
|
-
scp: undefined,
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const user = createMockUserFromToken(token);
|
|
33
|
-
|
|
34
|
-
expect(user.name).toBeUndefined();
|
|
35
|
-
expect(user.username).toBeUndefined();
|
|
36
|
-
expect(user.scopes).toBeUndefined();
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it('throws for a token with no payload segment', () => {
|
|
40
|
-
expect(() => createMockUserFromToken('not-a-jwt')).toThrow(/payload segment/);
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
it('throws for an empty string', () => {
|
|
44
|
-
expect(() => createMockUserFromToken('')).toThrow(/payload segment/);
|
|
45
|
-
});
|
|
46
|
-
});
|