@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.
Files changed (32) hide show
  1. package/CHANGELOG.md +67 -120
  2. package/dist/esm/__tests__/mock/create-mock-user-from-token.test.js +39 -0
  3. package/dist/esm/__tests__/mock/create-mock-user-from-token.test.js.map +1 -0
  4. package/dist/esm/__tests__/mock/msal-mock.test.js +53 -1
  5. package/dist/esm/__tests__/mock/msal-mock.test.js.map +1 -1
  6. package/dist/esm/mock/MsalMockClient.js +25 -8
  7. package/dist/esm/mock/MsalMockClient.js.map +1 -1
  8. package/dist/esm/mock/MsalMockConfigurator.js +63 -11
  9. package/dist/esm/mock/MsalMockConfigurator.js.map +1 -1
  10. package/dist/esm/mock/create-mock-user-from-token.js +40 -0
  11. package/dist/esm/mock/create-mock-user-from-token.js.map +1 -0
  12. package/dist/esm/mock/index.js +1 -0
  13. package/dist/esm/mock/index.js.map +1 -1
  14. package/dist/esm/version.js +1 -1
  15. package/dist/esm/version.js.map +1 -1
  16. package/dist/tsconfig.tsbuildinfo +1 -1
  17. package/dist/types/__tests__/mock/create-mock-user-from-token.test.d.ts +1 -0
  18. package/dist/types/mock/MsalMockClient.d.ts +12 -0
  19. package/dist/types/mock/MsalMockConfigurator.d.ts +30 -0
  20. package/dist/types/mock/create-mock-user-from-token.d.ts +25 -0
  21. package/dist/types/mock/index.d.ts +1 -0
  22. package/dist/types/version.d.ts +1 -1
  23. package/docs/testing.md +24 -0
  24. package/package.json +5 -5
  25. package/src/__tests__/mock/create-mock-user-from-token.test.ts +46 -0
  26. package/src/__tests__/mock/msal-mock.test.ts +69 -0
  27. package/src/mock/MsalMockClient.ts +27 -8
  28. package/src/mock/MsalMockConfigurator.ts +78 -14
  29. package/src/mock/create-mock-user-from-token.ts +46 -0
  30. package/src/mock/index.ts +1 -0
  31. package/src/version.ts +1 -1
  32. 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
- * Signs the declared user in on the client the module authenticates through.
100
+ * Declares the token to return, independent of who is signed in.
100
101
  *
101
102
  * @remarks
102
- * Deliberately not done while the client is built: that would assume the scope
103
- * declaring the user is the scope building the client, which is exactly what
104
- * is not true when an application is tested inside a portal. The host built
105
- * that client, in a scope this builder never sees, so the client has to be
106
- * located rather than assumed.
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
- #signIn(account, config, init) {
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 account state cannot be applied to it.
150
+ // Reject a real client because mock state cannot be applied to it.
117
151
  if (!(client instanceof MsalMockClient)) {
118
- throw new Error('MsalMockConfigurator: cannot sign a user in, because this module does not authenticate through a mock client. Declare the user where that client is configured instead.');
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.setUser(account);
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;AA2BrE;;;;;;;GAOG;AACH,MAAM,uBAAuB,GAAqB;IAChD,IAAI,EAAE;QACJ,QAAQ,EAAE,oBAAoB;QAC9B,QAAQ,EAAE,oBAAoB;KAC/B;CACF,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,OAAO,oBAAqB,SAAQ,gBAAgB;IACxD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCG;IACI,UAAU,CACf,OAAyE;QAEzE,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,OAAO,CAAC,CAAC;QACnC,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,OAAO,CACL,OAA4B,EAC5B,MAAkB,EAClB,IAAgC;QAEhC,MAAM,IAAI,GAAI,IAAI,EAAE,GAA4C,EAAE,IAAI,CAAC;QACvE,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,IAAI,EAAE,MAAM,CAAC;QAE7C,2EAA2E;QAC3E,IAAI,CAAC,CAAC,MAAM,YAAY,cAAc,CAAC,EAAE,CAAC;YACxC,MAAM,IAAI,KAAK,CACb,yKAAyK,CAC1K,CAAC;QACJ,CAAC;QAED,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1B,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACM,KAAK,CAAC,cAAc,CAC3B,SAAqB,EACrB,IAAgC;QAEhC,2EAA2E;QAC3E,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,EAAE,CAAC;YACtD,IAAI,CAAC,eAAe,CAAC,uBAAuB,CAAC,CAAC;QAChD,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAE3D,2EAA2E;QAC3E,2DAA2D;QAC3D,MAAM,OAAO,GAAG,SAAS,CAAC,IAAI,EAAE,OAAO,CAAC;QACxC,4EAA4E;QAC5E,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAC1B,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACtC,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAED;;;;;;;;;;;;;;;;;;;;OAoBG;IACgB,KAAK,CAAC,aAAa,CAAC,MAAkB;QACvD,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,IAAI,uBAAuB,CAAC,CAAC;IACzF,CAAC;CACF"}
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"}
@@ -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"}
@@ -1,3 +1,3 @@
1
1
  // Generated by genversion.
2
- export const version = '11.0.0-next.0';
2
+ export const version = '11.0.0';
3
3
  //# sourceMappingURL=version.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,OAAO,GAAG,eAAe,CAAC"}
1
+ {"version":3,"file":"version.js","sourceRoot":"","sources":["../../src/version.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,MAAM,CAAC,MAAM,OAAO,GAAG,QAAQ,CAAC"}