@equinor/fusion-framework-module-msal 11.0.0-next.0 → 11.0.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 +67 -120
- package/dist/esm/__tests__/mock/create-mock-user-from-token.test.js +39 -0
- package/dist/esm/__tests__/mock/create-mock-user-from-token.test.js.map +1 -0
- package/dist/esm/__tests__/mock/msal-mock.test.js +53 -1
- package/dist/esm/__tests__/mock/msal-mock.test.js.map +1 -1
- package/dist/esm/mock/MsalMockClient.js +25 -8
- package/dist/esm/mock/MsalMockClient.js.map +1 -1
- package/dist/esm/mock/MsalMockConfigurator.js +63 -11
- package/dist/esm/mock/MsalMockConfigurator.js.map +1 -1
- package/dist/esm/mock/create-mock-user-from-token.js +40 -0
- package/dist/esm/mock/create-mock-user-from-token.js.map +1 -0
- package/dist/esm/mock/index.js +1 -0
- package/dist/esm/mock/index.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/__tests__/mock/create-mock-user-from-token.test.d.ts +1 -0
- package/dist/types/mock/MsalMockClient.d.ts +12 -0
- package/dist/types/mock/MsalMockConfigurator.d.ts +30 -0
- package/dist/types/mock/create-mock-user-from-token.d.ts +25 -0
- package/dist/types/mock/index.d.ts +1 -0
- package/dist/types/version.d.ts +1 -1
- package/docs/testing.md +24 -0
- package/package.json +5 -5
- package/src/__tests__/mock/create-mock-user-from-token.test.ts +46 -0
- package/src/__tests__/mock/msal-mock.test.ts +69 -0
- package/src/mock/MsalMockClient.ts +27 -8
- package/src/mock/MsalMockConfigurator.ts +78 -14
- package/src/mock/create-mock-user-from-token.ts +46 -0
- package/src/mock/index.ts +1 -0
- package/src/version.ts +1 -1
- package/.changeset/msal-auth-provider-fix.md +0 -7
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MsalConfigurator } from '../MsalConfigurator';
|
|
2
2
|
import { MsalMockClient } from './MsalMockClient';
|
|
3
|
+
import { createMockUserFromToken } from './create-mock-user-from-token';
|
|
3
4
|
/**
|
|
4
5
|
* The client configuration used when a test declares none.
|
|
5
6
|
*
|
|
@@ -96,28 +97,72 @@ export class MsalMockConfigurator extends MsalConfigurator {
|
|
|
96
97
|
return this;
|
|
97
98
|
}
|
|
98
99
|
/**
|
|
99
|
-
*
|
|
100
|
+
* Declares the token to return, independent of who is signed in.
|
|
100
101
|
*
|
|
101
102
|
* @remarks
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
103
|
+
* Use this when a backend mock validates its own tokens (specific claims, an
|
|
104
|
+
* audience, or a signature) — the client then returns this token verbatim
|
|
105
|
+
* instead of fabricating one from the signed-in user's fields.
|
|
106
|
+
*
|
|
107
|
+
* @param token - A JWT (e.g. from `createMockToken`, or issued by an external mock).
|
|
108
|
+
* @param skipResolve - When `true`, override only the token and leave an account
|
|
109
|
+
* declared through {@link setAccount} untouched. Defaults to `false`, which also signs
|
|
110
|
+
* in the user described by the token's claims, via {@link createMockUserFromToken}.
|
|
111
|
+
* @returns The builder, for chaining.
|
|
112
|
+
*
|
|
113
|
+
* @example Sign in as whoever the token names
|
|
114
|
+
* ```typescript
|
|
115
|
+
* builder.setToken(token);
|
|
116
|
+
* ```
|
|
117
|
+
*
|
|
118
|
+
* @example Keep a separately declared account, but return this exact token
|
|
119
|
+
* ```typescript
|
|
120
|
+
* builder.setAccount({ name: 'Ada Lovelace' }).setToken(token, true);
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
setToken(token, skipResolve = false) {
|
|
124
|
+
this._set('mock.token', token);
|
|
125
|
+
// skipResolve defaults to false - most callers want the token's claims to name who is signed in
|
|
126
|
+
if (!skipResolve) {
|
|
127
|
+
this.setAccount(createMockUserFromToken(token));
|
|
128
|
+
}
|
|
129
|
+
return this;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Resolves the client the module authenticates through, wherever it was built.
|
|
133
|
+
*
|
|
134
|
+
* @remarks
|
|
135
|
+
* Shared by {@link setAccount} and {@link setToken} application: neither can
|
|
136
|
+
* assume the scope declaring mock state is the scope that built the client,
|
|
137
|
+
* which is exactly what is not true when an application is tested inside a
|
|
138
|
+
* portal. The host built that client, in a scope this builder never sees, so
|
|
139
|
+
* the client has to be located rather than assumed.
|
|
107
140
|
*
|
|
108
|
-
* @param account - The user to sign in, or `null` when nobody is.
|
|
109
141
|
* @param config - The validated configuration, carrying the client when one was built.
|
|
110
142
|
* @param init - The builder arguments, carrying the host reference when hoisted.
|
|
143
|
+
* @param action - Describes what could not be applied, for the thrown error.
|
|
144
|
+
* @returns The resolved mock client.
|
|
111
145
|
* @throws When the resolved client is not a {@link MsalMockClient}.
|
|
112
146
|
*/
|
|
113
|
-
#
|
|
147
|
+
#getClient(config, init, action) {
|
|
114
148
|
const host = init?.ref?.auth;
|
|
115
149
|
const client = config.client ?? host?.client;
|
|
116
|
-
// Reject a real client because mock
|
|
150
|
+
// Reject a real client because mock state cannot be applied to it.
|
|
117
151
|
if (!(client instanceof MsalMockClient)) {
|
|
118
|
-
throw new Error(
|
|
152
|
+
throw new Error(`MsalMockConfigurator: cannot ${action}, because this module does not authenticate through a mock client. Declare it where that client is configured instead.`);
|
|
119
153
|
}
|
|
120
|
-
client
|
|
154
|
+
return client;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Signs the declared user in on the client the module authenticates through.
|
|
158
|
+
*
|
|
159
|
+
* @param account - The user to sign in, or `null` when nobody is.
|
|
160
|
+
* @param config - The validated configuration, carrying the client when one was built.
|
|
161
|
+
* @param init - The builder arguments, carrying the host reference when hoisted.
|
|
162
|
+
* @throws When the resolved client is not a {@link MsalMockClient}.
|
|
163
|
+
*/
|
|
164
|
+
#signIn(account, config, init) {
|
|
165
|
+
this.#getClient(config, init, 'sign a user in').setUser(account);
|
|
121
166
|
}
|
|
122
167
|
/**
|
|
123
168
|
* Assembles the configuration, then signs the declared user in.
|
|
@@ -157,6 +202,13 @@ export class MsalMockConfigurator extends MsalConfigurator {
|
|
|
157
202
|
if (account !== undefined) {
|
|
158
203
|
this.#signIn(account, config, init);
|
|
159
204
|
}
|
|
205
|
+
// Applied after the account so a token declared alongside `skipResolve: true`
|
|
206
|
+
// overrides whatever `setUser` above just fabricated.
|
|
207
|
+
const token = rawConfig.mock?.token;
|
|
208
|
+
// absent means the test declared no token override; leave the client generating its own
|
|
209
|
+
if (token !== undefined) {
|
|
210
|
+
this.#getClient(config, init, 'set a token').setToken(token);
|
|
211
|
+
}
|
|
160
212
|
return config;
|
|
161
213
|
}
|
|
162
214
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MsalMockConfigurator.js","sourceRoot":"","sources":["../../../src/mock/MsalMockConfigurator.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,gBAAgB,EAAmB,MAAM,qBAAqB,CAAC;AAExE,OAAO,EAAE,cAAc,EAAqB,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"MsalMockConfigurator.js","sourceRoot":"","sources":["../../../src/mock/MsalMockConfigurator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { decodeJwtSegment } from './decode-jwt-segment';
|
|
2
|
+
/**
|
|
3
|
+
* Derives a {@link MsalMockUser} from a JWT's payload claims, so a token minted
|
|
4
|
+
* outside this module (e.g. by a backend's own mock) can drive who the mock
|
|
5
|
+
* signs in as.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Maps the standard Entra ID claims Fusion applications read — `name`,
|
|
9
|
+
* `preferred_username`, `oid`, `tid`, `scp` — onto the matching
|
|
10
|
+
* {@link MsalMockUser} fields. Identity only: it does not affect which token
|
|
11
|
+
* the client returns — use {@link MsalMockConfigurator.setToken} for that.
|
|
12
|
+
*
|
|
13
|
+
* @param token - A JWT (e.g. from {@link createMockToken}, or issued by an
|
|
14
|
+
* external mock) with a base64url-encoded payload segment.
|
|
15
|
+
* @returns A mock user built from the token's claims.
|
|
16
|
+
* @throws When the token has no payload segment (`header.payload.signature`).
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```typescript
|
|
20
|
+
* enableMsalMock(configurator, (builder) => {
|
|
21
|
+
* builder.setAccount(createMockUserFromToken(token));
|
|
22
|
+
* });
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export const createMockUserFromToken = (token) => {
|
|
26
|
+
const [, payload] = token.split('.');
|
|
27
|
+
// fail loudly rather than signing in an empty/garbage user from a malformed token
|
|
28
|
+
if (!payload) {
|
|
29
|
+
throw new Error('createMockUserFromToken: expected a JWT with a payload segment (header.payload.signature)');
|
|
30
|
+
}
|
|
31
|
+
const claims = JSON.parse(decodeJwtSegment(payload));
|
|
32
|
+
return {
|
|
33
|
+
name: claims.name,
|
|
34
|
+
username: claims.preferred_username,
|
|
35
|
+
userId: claims.oid,
|
|
36
|
+
tenantId: claims.tid,
|
|
37
|
+
scopes: claims.scp?.split(' '),
|
|
38
|
+
};
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=create-mock-user-from-token.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-mock-user-from-token.js","sourceRoot":"","sources":["../../../src/mock/create-mock-user-from-token.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAGxD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,KAAa,EAAgB,EAAE;IACrE,MAAM,CAAC,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACrC,kFAAkF;IAClF,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,2FAA2F,CAC5F,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAoB,IAAI,CAAC,KAAK,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;IAEtE,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,QAAQ,EAAE,MAAM,CAAC,kBAAkB;QACnC,MAAM,EAAE,MAAM,CAAC,GAAG;QAClB,QAAQ,EAAE,MAAM,CAAC,GAAG;QACpB,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC;KAC/B,CAAC;AACJ,CAAC,CAAC"}
|
package/dist/esm/mock/index.js
CHANGED
|
@@ -26,5 +26,6 @@ export { createMsalMockClient } from './create-msal-mock-client';
|
|
|
26
26
|
export { MsalMockConfigurator } from './MsalMockConfigurator';
|
|
27
27
|
export { enableMsalMock, msalMockModule } from './module';
|
|
28
28
|
export { createMockToken } from './create-mock-token';
|
|
29
|
+
export { createMockUserFromToken } from './create-mock-user-from-token';
|
|
29
30
|
export { decodeJwtSegment } from './decode-jwt-segment';
|
|
30
31
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mock/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,cAAc,EAAqB,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,cAAc,EAAyB,MAAM,UAAU,CAAC;AACjF,OAAO,EAAE,eAAe,EAAwB,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/mock/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,EAAE,cAAc,EAAqB,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,cAAc,EAAyB,MAAM,UAAU,CAAC;AACjF,OAAO,EAAE,eAAe,EAAwB,MAAM,qBAAqB,CAAC;AAC5E,OAAO,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC"}
|
package/dist/esm/version.js
CHANGED
package/dist/esm/version.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC"}
|