@azure/msal-browser 3.0.2 → 3.1.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 (256) hide show
  1. package/dist/app/IPublicClientApplication.d.ts +2 -0
  2. package/dist/app/IPublicClientApplication.d.ts.map +1 -1
  3. package/dist/app/IPublicClientApplication.mjs +4 -1
  4. package/dist/app/IPublicClientApplication.mjs.map +1 -1
  5. package/dist/app/PublicClientApplication.d.ts +6 -0
  6. package/dist/app/PublicClientApplication.d.ts.map +1 -1
  7. package/dist/app/PublicClientApplication.mjs +8 -1
  8. package/dist/app/PublicClientApplication.mjs.map +1 -1
  9. package/dist/broker/nativeBroker/NativeMessageHandler.d.ts.map +1 -1
  10. package/dist/broker/nativeBroker/NativeMessageHandler.mjs +5 -4
  11. package/dist/broker/nativeBroker/NativeMessageHandler.mjs.map +1 -1
  12. package/dist/cache/AsyncMemoryStorage.mjs +4 -3
  13. package/dist/cache/AsyncMemoryStorage.mjs.map +1 -1
  14. package/dist/cache/BrowserCacheManager.d.ts +9 -2
  15. package/dist/cache/BrowserCacheManager.d.ts.map +1 -1
  16. package/dist/cache/BrowserCacheManager.mjs +54 -26
  17. package/dist/cache/BrowserCacheManager.mjs.map +1 -1
  18. package/dist/cache/BrowserStorage.mjs +1 -1
  19. package/dist/cache/BrowserStorage.mjs.map +1 -1
  20. package/dist/cache/CryptoKeyStore.mjs +1 -1
  21. package/dist/cache/CryptoKeyStore.mjs.map +1 -1
  22. package/dist/cache/DatabaseStorage.d.ts.map +1 -1
  23. package/dist/cache/DatabaseStorage.mjs +9 -8
  24. package/dist/cache/DatabaseStorage.mjs.map +1 -1
  25. package/dist/cache/MemoryStorage.mjs +1 -1
  26. package/dist/cache/MemoryStorage.mjs.map +1 -1
  27. package/dist/cache/TokenCache.d.ts.map +1 -1
  28. package/dist/cache/TokenCache.mjs +30 -30
  29. package/dist/cache/TokenCache.mjs.map +1 -1
  30. package/dist/config/Configuration.d.ts +1 -0
  31. package/dist/config/Configuration.d.ts.map +1 -1
  32. package/dist/config/Configuration.mjs +7 -11
  33. package/dist/config/Configuration.mjs.map +1 -1
  34. package/dist/controllers/ControllerFactory.mjs +1 -1
  35. package/dist/controllers/ControllerFactory.mjs.map +1 -1
  36. package/dist/controllers/IController.d.ts +3 -27
  37. package/dist/controllers/IController.d.ts.map +1 -1
  38. package/dist/controllers/StandardController.d.ts +6 -25
  39. package/dist/controllers/StandardController.d.ts.map +1 -1
  40. package/dist/controllers/StandardController.mjs +28 -52
  41. package/dist/controllers/StandardController.mjs.map +1 -1
  42. package/dist/crypto/BrowserCrypto.d.ts.map +1 -1
  43. package/dist/crypto/BrowserCrypto.mjs +4 -3
  44. package/dist/crypto/BrowserCrypto.mjs.map +1 -1
  45. package/dist/crypto/CryptoOps.d.ts +0 -3
  46. package/dist/crypto/CryptoOps.d.ts.map +1 -1
  47. package/dist/crypto/CryptoOps.mjs +14 -17
  48. package/dist/crypto/CryptoOps.mjs.map +1 -1
  49. package/dist/crypto/ModernBrowserCrypto.mjs +1 -1
  50. package/dist/crypto/ModernBrowserCrypto.mjs.map +1 -1
  51. package/dist/crypto/PkceGenerator.d.ts +0 -1
  52. package/dist/crypto/PkceGenerator.d.ts.map +1 -1
  53. package/dist/crypto/PkceGenerator.mjs +8 -8
  54. package/dist/crypto/PkceGenerator.mjs.map +1 -1
  55. package/dist/crypto/SignedHttpRequest.mjs +1 -1
  56. package/dist/crypto/SignedHttpRequest.mjs.map +1 -1
  57. package/dist/encode/Base64Decode.d.ts +6 -19
  58. package/dist/encode/Base64Decode.d.ts.map +1 -1
  59. package/dist/encode/Base64Decode.mjs +17 -70
  60. package/dist/encode/Base64Decode.mjs.map +1 -1
  61. package/dist/encode/Base64Encode.d.ts +15 -27
  62. package/dist/encode/Base64Encode.d.ts.map +1 -1
  63. package/dist/encode/Base64Encode.mjs +36 -75
  64. package/dist/encode/Base64Encode.mjs.map +1 -1
  65. package/dist/error/BrowserAuthError.d.ts +54 -195
  66. package/dist/error/BrowserAuthError.d.ts.map +1 -1
  67. package/dist/error/BrowserAuthError.mjs +150 -381
  68. package/dist/error/BrowserAuthError.mjs.map +1 -1
  69. package/dist/error/BrowserAuthErrorCodes.d.ts +45 -0
  70. package/dist/error/BrowserAuthErrorCodes.d.ts.map +1 -0
  71. package/dist/error/BrowserAuthErrorCodes.mjs +53 -0
  72. package/dist/error/BrowserAuthErrorCodes.mjs.map +1 -0
  73. package/dist/error/BrowserConfigurationAuthError.mjs +1 -1
  74. package/dist/error/BrowserConfigurationAuthError.mjs.map +1 -1
  75. package/dist/error/NativeAuthError.d.ts.map +1 -1
  76. package/dist/error/NativeAuthError.mjs +5 -4
  77. package/dist/error/NativeAuthError.mjs.map +1 -1
  78. package/dist/event/EventHandler.mjs +1 -1
  79. package/dist/event/EventHandler.mjs.map +1 -1
  80. package/dist/event/EventMessage.mjs +1 -1
  81. package/dist/event/EventMessage.mjs.map +1 -1
  82. package/dist/event/EventType.mjs +1 -1
  83. package/dist/event/EventType.mjs.map +1 -1
  84. package/dist/index.d.ts +5 -5
  85. package/dist/index.d.ts.map +1 -1
  86. package/dist/index.mjs +5 -3
  87. package/dist/index.mjs.map +1 -1
  88. package/dist/interaction_client/BaseInteractionClient.d.ts +2 -1
  89. package/dist/interaction_client/BaseInteractionClient.d.ts.map +1 -1
  90. package/dist/interaction_client/BaseInteractionClient.mjs +1 -1
  91. package/dist/interaction_client/BaseInteractionClient.mjs.map +1 -1
  92. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs +1 -1
  93. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs.map +1 -1
  94. package/dist/interaction_client/NativeInteractionClient.d.ts +4 -10
  95. package/dist/interaction_client/NativeInteractionClient.d.ts.map +1 -1
  96. package/dist/interaction_client/NativeInteractionClient.mjs +22 -30
  97. package/dist/interaction_client/NativeInteractionClient.mjs.map +1 -1
  98. package/dist/interaction_client/PopupClient.d.ts.map +1 -1
  99. package/dist/interaction_client/PopupClient.mjs +16 -15
  100. package/dist/interaction_client/PopupClient.mjs.map +1 -1
  101. package/dist/interaction_client/RedirectClient.d.ts.map +1 -1
  102. package/dist/interaction_client/RedirectClient.mjs +7 -6
  103. package/dist/interaction_client/RedirectClient.mjs.map +1 -1
  104. package/dist/interaction_client/SilentAuthCodeClient.d.ts.map +1 -1
  105. package/dist/interaction_client/SilentAuthCodeClient.mjs +5 -4
  106. package/dist/interaction_client/SilentAuthCodeClient.mjs.map +1 -1
  107. package/dist/interaction_client/SilentCacheClient.d.ts +4 -2
  108. package/dist/interaction_client/SilentCacheClient.d.ts.map +1 -1
  109. package/dist/interaction_client/SilentCacheClient.mjs +10 -8
  110. package/dist/interaction_client/SilentCacheClient.mjs.map +1 -1
  111. package/dist/interaction_client/SilentIframeClient.d.ts.map +1 -1
  112. package/dist/interaction_client/SilentIframeClient.mjs +20 -47
  113. package/dist/interaction_client/SilentIframeClient.mjs.map +1 -1
  114. package/dist/interaction_client/SilentRefreshClient.d.ts.map +1 -1
  115. package/dist/interaction_client/SilentRefreshClient.mjs +4 -3
  116. package/dist/interaction_client/SilentRefreshClient.mjs.map +1 -1
  117. package/dist/interaction_client/StandardInteractionClient.d.ts.map +1 -1
  118. package/dist/interaction_client/StandardInteractionClient.mjs +8 -7
  119. package/dist/interaction_client/StandardInteractionClient.mjs.map +1 -1
  120. package/dist/interaction_handler/InteractionHandler.d.ts.map +1 -1
  121. package/dist/interaction_handler/InteractionHandler.mjs +9 -9
  122. package/dist/interaction_handler/InteractionHandler.mjs.map +1 -1
  123. package/dist/interaction_handler/RedirectHandler.d.ts.map +1 -1
  124. package/dist/interaction_handler/RedirectHandler.mjs +11 -10
  125. package/dist/interaction_handler/RedirectHandler.mjs.map +1 -1
  126. package/dist/interaction_handler/SilentHandler.d.ts.map +1 -1
  127. package/dist/interaction_handler/SilentHandler.mjs +10 -9
  128. package/dist/interaction_handler/SilentHandler.mjs.map +1 -1
  129. package/dist/navigation/NavigationClient.mjs +1 -1
  130. package/dist/navigation/NavigationClient.mjs.map +1 -1
  131. package/dist/network/FetchClient.d.ts.map +1 -1
  132. package/dist/network/FetchClient.mjs +9 -8
  133. package/dist/network/FetchClient.mjs.map +1 -1
  134. package/dist/operatingcontext/BaseOperatingContext.mjs +1 -1
  135. package/dist/operatingcontext/BaseOperatingContext.mjs.map +1 -1
  136. package/dist/operatingcontext/StandardOperatingContext.mjs +1 -1
  137. package/dist/operatingcontext/StandardOperatingContext.mjs.map +1 -1
  138. package/dist/operatingcontext/TeamsAppOperatingContext.mjs +1 -1
  139. package/dist/operatingcontext/TeamsAppOperatingContext.mjs.map +1 -1
  140. package/dist/packageMetadata.d.ts +1 -1
  141. package/dist/packageMetadata.mjs +2 -2
  142. package/dist/packageMetadata.mjs.map +1 -1
  143. package/dist/request/ClearCacheRequest.d.ts +11 -0
  144. package/dist/request/ClearCacheRequest.d.ts.map +1 -0
  145. package/dist/telemetry/BrowserPerformanceClient.d.ts +2 -4
  146. package/dist/telemetry/BrowserPerformanceClient.d.ts.map +1 -1
  147. package/dist/telemetry/BrowserPerformanceClient.mjs +2 -6
  148. package/dist/telemetry/BrowserPerformanceClient.mjs.map +1 -1
  149. package/dist/telemetry/BrowserPerformanceMeasurement.mjs +1 -1
  150. package/dist/telemetry/BrowserPerformanceMeasurement.mjs.map +1 -1
  151. package/dist/utils/BrowserConstants.mjs +1 -1
  152. package/dist/utils/BrowserConstants.mjs.map +1 -1
  153. package/dist/utils/BrowserProtocolUtils.d.ts.map +1 -1
  154. package/dist/utils/BrowserProtocolUtils.mjs +3 -3
  155. package/dist/utils/BrowserProtocolUtils.mjs.map +1 -1
  156. package/dist/utils/BrowserStringUtils.mjs +1 -1
  157. package/dist/utils/BrowserStringUtils.mjs.map +1 -1
  158. package/dist/utils/BrowserUtils.d.ts +0 -9
  159. package/dist/utils/BrowserUtils.d.ts.map +1 -1
  160. package/dist/utils/BrowserUtils.mjs +8 -33
  161. package/dist/utils/BrowserUtils.mjs.map +1 -1
  162. package/lib/msal-browser.cjs +6579 -7102
  163. package/lib/msal-browser.cjs.map +1 -1
  164. package/lib/msal-browser.js +6579 -7102
  165. package/lib/msal-browser.js.map +1 -1
  166. package/lib/msal-browser.min.js +65 -62
  167. package/package.json +7 -5
  168. package/src/app/IPublicClientApplication.ts +199 -0
  169. package/src/app/PublicClientApplication.ts +405 -0
  170. package/src/broker/nativeBroker/NativeMessageHandler.ts +419 -0
  171. package/src/broker/nativeBroker/NativeRequest.ts +52 -0
  172. package/src/broker/nativeBroker/NativeResponse.ts +56 -0
  173. package/src/cache/AsyncMemoryStorage.ts +158 -0
  174. package/src/cache/BrowserCacheManager.ts +1994 -0
  175. package/src/cache/BrowserStorage.ts +54 -0
  176. package/src/cache/CryptoKeyStore.ts +63 -0
  177. package/src/cache/DatabaseStorage.ts +287 -0
  178. package/src/cache/IAsyncMemoryStorage.ts +36 -0
  179. package/src/cache/ITokenCache.ts +21 -0
  180. package/src/cache/IWindowStorage.ts +36 -0
  181. package/src/cache/MemoryStorage.ts +42 -0
  182. package/src/cache/TokenCache.ts +484 -0
  183. package/src/config/Configuration.ts +375 -0
  184. package/src/controllers/ControllerFactory.ts +55 -0
  185. package/src/controllers/IController.ts +131 -0
  186. package/src/controllers/StandardController.ts +2107 -0
  187. package/src/crypto/BrowserCrypto.ts +135 -0
  188. package/src/crypto/CryptoOps.ts +247 -0
  189. package/src/crypto/ISubtleCrypto.ts +30 -0
  190. package/src/crypto/ModernBrowserCrypto.ts +69 -0
  191. package/src/crypto/PkceGenerator.ts +76 -0
  192. package/src/crypto/SignedHttpRequest.ts +76 -0
  193. package/src/encode/Base64Decode.ts +26 -0
  194. package/src/encode/Base64Encode.ts +52 -0
  195. package/src/error/BrowserAuthError.ts +340 -0
  196. package/src/error/BrowserAuthErrorCodes.ts +56 -0
  197. package/src/error/BrowserConfigurationAuthError.ts +133 -0
  198. package/src/error/NativeAuthError.ts +141 -0
  199. package/src/event/EventHandler.ts +176 -0
  200. package/src/event/EventMessage.ts +125 -0
  201. package/src/event/EventType.ts +33 -0
  202. package/src/index.ts +152 -0
  203. package/src/interaction_client/BaseInteractionClient.ts +301 -0
  204. package/src/interaction_client/HybridSpaAuthorizationCodeClient.ts +16 -0
  205. package/src/interaction_client/NativeInteractionClient.ts +907 -0
  206. package/src/interaction_client/PopupClient.ts +926 -0
  207. package/src/interaction_client/RedirectClient.ts +616 -0
  208. package/src/interaction_client/SilentAuthCodeClient.ts +163 -0
  209. package/src/interaction_client/SilentCacheClient.ts +133 -0
  210. package/src/interaction_client/SilentIframeClient.ts +301 -0
  211. package/src/interaction_client/SilentRefreshClient.ts +120 -0
  212. package/src/interaction_client/StandardInteractionClient.ts +481 -0
  213. package/src/interaction_handler/InteractionHandler.ts +246 -0
  214. package/src/interaction_handler/RedirectHandler.ts +225 -0
  215. package/src/interaction_handler/SilentHandler.ts +266 -0
  216. package/src/navigation/INavigationClient.ts +23 -0
  217. package/src/navigation/NavigationClient.ts +55 -0
  218. package/src/navigation/NavigationOptions.ts +18 -0
  219. package/src/network/FetchClient.ts +130 -0
  220. package/src/operatingcontext/BaseOperatingContext.ts +82 -0
  221. package/src/operatingcontext/StandardOperatingContext.ts +49 -0
  222. package/src/operatingcontext/TeamsAppOperatingContext.ts +48 -0
  223. package/src/packageMetadata.ts +3 -0
  224. package/src/request/AuthorizationCodeRequest.ts +19 -0
  225. package/src/request/AuthorizationUrlRequest.ts +17 -0
  226. package/src/request/ClearCacheRequest.ts +16 -0
  227. package/src/request/EndSessionPopupRequest.ts +26 -0
  228. package/src/request/EndSessionRequest.ts +23 -0
  229. package/src/request/PopupRequest.ts +51 -0
  230. package/src/request/PopupWindowAttributes.ts +22 -0
  231. package/src/request/RedirectRequest.ts +51 -0
  232. package/src/request/SilentRequest.ts +49 -0
  233. package/src/request/SsoSilentRequest.ts +44 -0
  234. package/src/response/AuthenticationResult.ts +13 -0
  235. package/src/telemetry/BrowserPerformanceClient.ts +226 -0
  236. package/src/telemetry/BrowserPerformanceMeasurement.ts +144 -0
  237. package/src/utils/BrowserConstants.ts +241 -0
  238. package/src/utils/BrowserProtocolUtils.ts +57 -0
  239. package/src/utils/BrowserStringUtils.ts +143 -0
  240. package/src/utils/BrowserUtils.ts +156 -0
  241. package/dist/crypto/GuidGenerator.d.ts +0 -13
  242. package/dist/crypto/GuidGenerator.d.ts.map +0 -1
  243. package/dist/crypto/GuidGenerator.mjs +0 -104
  244. package/dist/crypto/GuidGenerator.mjs.map +0 -1
  245. package/dist/internals.d.ts +0 -23
  246. package/dist/internals.d.ts.map +0 -1
  247. package/dist/internals.mjs +0 -28
  248. package/dist/internals.mjs.map +0 -1
  249. package/dist/network/XhrClient.d.ts +0 -41
  250. package/dist/network/XhrClient.d.ts.map +0 -1
  251. package/dist/network/XhrClient.mjs +0 -125
  252. package/dist/network/XhrClient.mjs.map +0 -1
  253. package/dist/utils/MathUtils.d.ts +0 -12
  254. package/dist/utils/MathUtils.d.ts.map +0 -1
  255. package/dist/utils/MathUtils.mjs +0 -26
  256. package/dist/utils/MathUtils.mjs.map +0 -1
@@ -0,0 +1,419 @@
1
+ /*
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import {
7
+ NativeConstants,
8
+ NativeExtensionMethod,
9
+ } from "../../utils/BrowserConstants";
10
+ import {
11
+ Logger,
12
+ AuthError,
13
+ AuthenticationScheme,
14
+ InProgressPerformanceEvent,
15
+ PerformanceEvents,
16
+ IPerformanceClient,
17
+ ICrypto,
18
+ } from "@azure/msal-common";
19
+ import {
20
+ NativeExtensionRequest,
21
+ NativeExtensionRequestBody,
22
+ } from "./NativeRequest";
23
+ import { NativeAuthError } from "../../error/NativeAuthError";
24
+ import {
25
+ createBrowserAuthError,
26
+ BrowserAuthErrorCodes,
27
+ } from "../../error/BrowserAuthError";
28
+ import { BrowserConfiguration } from "../../config/Configuration";
29
+
30
+ type ResponseResolvers<T> = {
31
+ resolve: (value: T | PromiseLike<T>) => void;
32
+ reject: (
33
+ value: AuthError | Error | PromiseLike<Error> | PromiseLike<AuthError>
34
+ ) => void;
35
+ };
36
+
37
+ export class NativeMessageHandler {
38
+ private extensionId: string | undefined;
39
+ private extensionVersion: string | undefined;
40
+ private logger: Logger;
41
+ private crypto: ICrypto;
42
+ private readonly handshakeTimeoutMs: number;
43
+ private timeoutId: number | undefined;
44
+ private resolvers: Map<string, ResponseResolvers<object>>;
45
+ private handshakeResolvers: Map<string, ResponseResolvers<void>>;
46
+ private messageChannel: MessageChannel;
47
+ private readonly windowListener: (event: MessageEvent) => void;
48
+ private readonly performanceClient: IPerformanceClient;
49
+ private readonly handshakeEvent: InProgressPerformanceEvent;
50
+
51
+ constructor(
52
+ logger: Logger,
53
+ handshakeTimeoutMs: number,
54
+ performanceClient: IPerformanceClient,
55
+ crypto: ICrypto,
56
+ extensionId?: string
57
+ ) {
58
+ this.logger = logger;
59
+ this.handshakeTimeoutMs = handshakeTimeoutMs;
60
+ this.extensionId = extensionId;
61
+ this.resolvers = new Map(); // Used for non-handshake messages
62
+ this.handshakeResolvers = new Map(); // Used for handshake messages
63
+ this.messageChannel = new MessageChannel();
64
+ this.windowListener = this.onWindowMessage.bind(this); // Window event callback doesn't have access to 'this' unless it's bound
65
+ this.performanceClient = performanceClient;
66
+ this.handshakeEvent = performanceClient.startMeasurement(
67
+ PerformanceEvents.NativeMessageHandlerHandshake
68
+ );
69
+ this.crypto = crypto;
70
+ }
71
+
72
+ /**
73
+ * Sends a given message to the extension and resolves with the extension response
74
+ * @param body
75
+ */
76
+ async sendMessage(body: NativeExtensionRequestBody): Promise<object> {
77
+ this.logger.trace("NativeMessageHandler - sendMessage called.");
78
+ const req: NativeExtensionRequest = {
79
+ channel: NativeConstants.CHANNEL_ID,
80
+ extensionId: this.extensionId,
81
+ responseId: this.crypto.createNewGuid(),
82
+ body: body,
83
+ };
84
+
85
+ this.logger.trace(
86
+ "NativeMessageHandler - Sending request to browser extension"
87
+ );
88
+ this.logger.tracePii(
89
+ `NativeMessageHandler - Sending request to browser extension: ${JSON.stringify(
90
+ req
91
+ )}`
92
+ );
93
+ this.messageChannel.port1.postMessage(req);
94
+
95
+ return new Promise((resolve, reject) => {
96
+ this.resolvers.set(req.responseId, { resolve, reject });
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Returns an instance of the MessageHandler that has successfully established a connection with an extension
102
+ * @param {Logger} logger
103
+ * @param {number} handshakeTimeoutMs
104
+ * @param {IPerformanceClient} performanceClient
105
+ * @param {ICrypto} crypto
106
+ */
107
+ static async createProvider(
108
+ logger: Logger,
109
+ handshakeTimeoutMs: number,
110
+ performanceClient: IPerformanceClient,
111
+ crypto: ICrypto
112
+ ): Promise<NativeMessageHandler> {
113
+ logger.trace("NativeMessageHandler - createProvider called.");
114
+ try {
115
+ const preferredProvider = new NativeMessageHandler(
116
+ logger,
117
+ handshakeTimeoutMs,
118
+ performanceClient,
119
+ crypto,
120
+ NativeConstants.PREFERRED_EXTENSION_ID
121
+ );
122
+ await preferredProvider.sendHandshakeRequest();
123
+ return preferredProvider;
124
+ } catch (e) {
125
+ // If preferred extension fails for whatever reason, fallback to using any installed extension
126
+ const backupProvider = new NativeMessageHandler(
127
+ logger,
128
+ handshakeTimeoutMs,
129
+ performanceClient,
130
+ crypto
131
+ );
132
+ await backupProvider.sendHandshakeRequest();
133
+ return backupProvider;
134
+ }
135
+ }
136
+
137
+ /**
138
+ * Send handshake request helper.
139
+ */
140
+ private async sendHandshakeRequest(): Promise<void> {
141
+ this.logger.trace(
142
+ "NativeMessageHandler - sendHandshakeRequest called."
143
+ );
144
+ // Register this event listener before sending handshake
145
+ window.addEventListener("message", this.windowListener, false); // false is important, because content script message processing should work first
146
+
147
+ const req: NativeExtensionRequest = {
148
+ channel: NativeConstants.CHANNEL_ID,
149
+ extensionId: this.extensionId,
150
+ responseId: this.crypto.createNewGuid(),
151
+ body: {
152
+ method: NativeExtensionMethod.HandshakeRequest,
153
+ },
154
+ };
155
+ this.handshakeEvent.add({
156
+ extensionId: this.extensionId,
157
+ extensionHandshakeTimeoutMs: this.handshakeTimeoutMs,
158
+ });
159
+
160
+ this.messageChannel.port1.onmessage = (event) => {
161
+ this.onChannelMessage(event);
162
+ };
163
+
164
+ window.postMessage(req, window.origin, [this.messageChannel.port2]);
165
+
166
+ return new Promise((resolve, reject) => {
167
+ this.handshakeResolvers.set(req.responseId, { resolve, reject });
168
+ this.timeoutId = window.setTimeout(() => {
169
+ /*
170
+ * Throw an error if neither HandshakeResponse nor original Handshake request are received in a reasonable timeframe.
171
+ * This typically suggests an event handler stopped propagation of the Handshake request but did not respond to it on the MessageChannel port
172
+ */
173
+ window.removeEventListener(
174
+ "message",
175
+ this.windowListener,
176
+ false
177
+ );
178
+ this.messageChannel.port1.close();
179
+ this.messageChannel.port2.close();
180
+ this.handshakeEvent.end({
181
+ extensionHandshakeTimedOut: true,
182
+ success: false,
183
+ });
184
+ reject(
185
+ createBrowserAuthError(
186
+ BrowserAuthErrorCodes.nativeHandshakeTimeout
187
+ )
188
+ );
189
+ this.handshakeResolvers.delete(req.responseId);
190
+ }, this.handshakeTimeoutMs); // Use a reasonable timeout in milliseconds here
191
+ });
192
+ }
193
+
194
+ /**
195
+ * Invoked when a message is posted to the window. If a handshake request is received it means the extension is not installed.
196
+ * @param event
197
+ */
198
+ private onWindowMessage(event: MessageEvent): void {
199
+ this.logger.trace("NativeMessageHandler - onWindowMessage called");
200
+ // We only accept messages from ourselves
201
+ if (event.source !== window) {
202
+ return;
203
+ }
204
+
205
+ const request = event.data;
206
+
207
+ if (
208
+ !request.channel ||
209
+ request.channel !== NativeConstants.CHANNEL_ID
210
+ ) {
211
+ return;
212
+ }
213
+
214
+ if (request.extensionId && request.extensionId !== this.extensionId) {
215
+ return;
216
+ }
217
+
218
+ if (request.body.method === NativeExtensionMethod.HandshakeRequest) {
219
+ const handshakeResolver = this.handshakeResolvers.get(
220
+ request.responseId
221
+ );
222
+ /*
223
+ * Filter out responses with no matched resolvers sooner to keep channel ports open while waiting for
224
+ * the proper response.
225
+ */
226
+ if (!handshakeResolver) {
227
+ this.logger.trace(
228
+ `NativeMessageHandler.onWindowMessage - resolver can't be found for request ${request.responseId}`
229
+ );
230
+ return;
231
+ }
232
+
233
+ // If we receive this message back it means no extension intercepted the request, meaning no extension supporting handshake protocol is installed
234
+ this.logger.verbose(
235
+ request.extensionId
236
+ ? `Extension with id: ${request.extensionId} not installed`
237
+ : "No extension installed"
238
+ );
239
+ clearTimeout(this.timeoutId);
240
+ this.messageChannel.port1.close();
241
+ this.messageChannel.port2.close();
242
+ window.removeEventListener("message", this.windowListener, false);
243
+ this.handshakeEvent.end({
244
+ success: false,
245
+ extensionInstalled: false,
246
+ });
247
+ handshakeResolver.reject(
248
+ createBrowserAuthError(
249
+ BrowserAuthErrorCodes.nativeExtensionNotInstalled
250
+ )
251
+ );
252
+ }
253
+ }
254
+
255
+ /**
256
+ * Invoked when a message is received from the extension on the MessageChannel port
257
+ * @param event
258
+ */
259
+ private onChannelMessage(event: MessageEvent): void {
260
+ this.logger.trace("NativeMessageHandler - onChannelMessage called.");
261
+ const request = event.data;
262
+
263
+ const resolver = this.resolvers.get(request.responseId);
264
+ const handshakeResolver = this.handshakeResolvers.get(
265
+ request.responseId
266
+ );
267
+
268
+ try {
269
+ const method = request.body.method;
270
+
271
+ if (method === NativeExtensionMethod.Response) {
272
+ if (!resolver) {
273
+ return;
274
+ }
275
+ const response = request.body.response;
276
+ this.logger.trace(
277
+ "NativeMessageHandler - Received response from browser extension"
278
+ );
279
+ this.logger.tracePii(
280
+ `NativeMessageHandler - Received response from browser extension: ${JSON.stringify(
281
+ response
282
+ )}`
283
+ );
284
+ if (response.status !== "Success") {
285
+ resolver.reject(
286
+ NativeAuthError.createError(
287
+ response.code,
288
+ response.description,
289
+ response.ext
290
+ )
291
+ );
292
+ } else if (response.result) {
293
+ if (
294
+ response.result["code"] &&
295
+ response.result["description"]
296
+ ) {
297
+ resolver.reject(
298
+ NativeAuthError.createError(
299
+ response.result["code"],
300
+ response.result["description"],
301
+ response.result["ext"]
302
+ )
303
+ );
304
+ } else {
305
+ resolver.resolve(response.result);
306
+ }
307
+ } else {
308
+ throw AuthError.createUnexpectedError(
309
+ "Event does not contain result."
310
+ );
311
+ }
312
+ this.resolvers.delete(request.responseId);
313
+ } else if (method === NativeExtensionMethod.HandshakeResponse) {
314
+ if (!handshakeResolver) {
315
+ this.logger.trace(
316
+ `NativeMessageHandler.onChannelMessage - resolver can't be found for request ${request.responseId}`
317
+ );
318
+ return;
319
+ }
320
+ clearTimeout(this.timeoutId); // Clear setTimeout
321
+ window.removeEventListener(
322
+ "message",
323
+ this.windowListener,
324
+ false
325
+ ); // Remove 'No extension' listener
326
+ this.extensionId = request.extensionId;
327
+ this.extensionVersion = request.body.version;
328
+ this.logger.verbose(
329
+ `NativeMessageHandler - Received HandshakeResponse from extension: ${this.extensionId}`
330
+ );
331
+ this.handshakeEvent.end({
332
+ extensionInstalled: true,
333
+ success: true,
334
+ });
335
+
336
+ handshakeResolver.resolve();
337
+ this.handshakeResolvers.delete(request.responseId);
338
+ }
339
+ // Do nothing if method is not Response or HandshakeResponse
340
+ } catch (err) {
341
+ this.logger.error("Error parsing response from WAM Extension");
342
+ this.logger.errorPii(
343
+ `Error parsing response from WAM Extension: ${err as string}`
344
+ );
345
+ this.logger.errorPii(`Unable to parse ${event}`);
346
+
347
+ if (resolver) {
348
+ resolver.reject(err as AuthError);
349
+ } else if (handshakeResolver) {
350
+ handshakeResolver.reject(err as AuthError);
351
+ }
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Returns the Id for the browser extension this handler is communicating with
357
+ * @returns
358
+ */
359
+ getExtensionId(): string | undefined {
360
+ return this.extensionId;
361
+ }
362
+
363
+ /**
364
+ * Returns the version for the browser extension this handler is communicating with
365
+ * @returns
366
+ */
367
+ getExtensionVersion(): string | undefined {
368
+ return this.extensionVersion;
369
+ }
370
+
371
+ /**
372
+ * Returns boolean indicating whether or not the request should attempt to use native broker
373
+ * @param logger
374
+ * @param config
375
+ * @param nativeExtensionProvider
376
+ * @param authenticationScheme
377
+ */
378
+ static isNativeAvailable(
379
+ config: BrowserConfiguration,
380
+ logger: Logger,
381
+ nativeExtensionProvider?: NativeMessageHandler,
382
+ authenticationScheme?: AuthenticationScheme
383
+ ): boolean {
384
+ logger.trace("isNativeAvailable called");
385
+ if (!config.system.allowNativeBroker) {
386
+ logger.trace(
387
+ "isNativeAvailable: allowNativeBroker is not enabled, returning false"
388
+ );
389
+ // Developer disabled WAM
390
+ return false;
391
+ }
392
+
393
+ if (!nativeExtensionProvider) {
394
+ logger.trace(
395
+ "isNativeAvailable: WAM extension provider is not initialized, returning false"
396
+ );
397
+ // Extension is not available
398
+ return false;
399
+ }
400
+
401
+ if (authenticationScheme) {
402
+ switch (authenticationScheme) {
403
+ case AuthenticationScheme.BEARER:
404
+ case AuthenticationScheme.POP:
405
+ logger.trace(
406
+ "isNativeAvailable: authenticationScheme is supported, returning true"
407
+ );
408
+ return true;
409
+ default:
410
+ logger.trace(
411
+ "isNativeAvailable: authenticationScheme is not supported, returning false"
412
+ );
413
+ return false;
414
+ }
415
+ }
416
+
417
+ return true;
418
+ }
419
+ }
@@ -0,0 +1,52 @@
1
+ /*
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { NativeExtensionMethod } from "../../utils/BrowserConstants";
7
+ import { StoreInCache, StringDict } from "@azure/msal-common";
8
+
9
+ /**
10
+ * Token request which native broker will use to acquire tokens
11
+ */
12
+ export type NativeTokenRequest = {
13
+ accountId: string; // WAM specific account id used for identification of WAM account. This can be any broker-id eventually
14
+ clientId: string;
15
+ authority: string;
16
+ redirectUri: string;
17
+ scope: string;
18
+ correlationId: string;
19
+ windowTitleSubstring: string; // The name of the document title. This helps the native prompt properly "parent" to the window making the request
20
+ prompt?: string;
21
+ nonce?: string;
22
+ claims?: string;
23
+ state?: string;
24
+ reqCnf?: string;
25
+ keyId?: string;
26
+ tokenType?: string;
27
+ shrClaims?: string;
28
+ shrNonce?: string;
29
+ resourceRequestMethod?: string;
30
+ resourceRequestUri?: string;
31
+ extendedExpiryToken?: boolean;
32
+ extraParameters?: StringDict;
33
+ storeInCache?: StoreInCache; // Object of booleans indicating whether to store tokens in the cache or not (default is true)
34
+ };
35
+
36
+ /**
37
+ * Request which will be forwarded to native broker by the browser extension
38
+ */
39
+ export type NativeExtensionRequestBody = {
40
+ method: NativeExtensionMethod;
41
+ request?: NativeTokenRequest;
42
+ };
43
+
44
+ /**
45
+ * Browser extension request
46
+ */
47
+ export type NativeExtensionRequest = {
48
+ channel: string;
49
+ responseId: string;
50
+ extensionId?: string;
51
+ body: NativeExtensionRequestBody;
52
+ };
@@ -0,0 +1,56 @@
1
+ /*
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ /**
7
+ * Account properties returned by Native Platform e.g. WAM
8
+ */
9
+ export type NativeAccountInfo = {
10
+ id: string;
11
+ properties: object;
12
+ userName: string;
13
+ };
14
+
15
+ /**
16
+ * Token response returned by Native Platform e.g. WAM
17
+ */
18
+ export type NativeResponse = {
19
+ access_token: string;
20
+ account: NativeAccountInfo;
21
+ client_info: string;
22
+ expires_in: number;
23
+ id_token: string;
24
+ properties: NativeResponseProperties;
25
+ scope: string;
26
+ state: string;
27
+ shr?: string;
28
+ extendedLifetimeToken?: boolean;
29
+ };
30
+
31
+ /**
32
+ * Properties returned under "properties" of the NativeResponse
33
+ */
34
+ export type NativeResponseProperties = {
35
+ MATS?: string;
36
+ };
37
+
38
+ /**
39
+ * The native token broker can optionally include additional information about operations it performs. If that data is returned, MSAL.js will include the following properties in the telemetry it collects.
40
+ */
41
+ export type MATS = {
42
+ is_cached?: number;
43
+ broker_version?: string;
44
+ account_join_on_start?: string;
45
+ account_join_on_end?: string;
46
+ device_join?: string;
47
+ prompt_behavior?: string;
48
+ api_error_code?: number;
49
+ ui_visible?: boolean;
50
+ silent_code?: number;
51
+ silent_bi_sub_code?: number;
52
+ silent_message?: string;
53
+ silent_status?: number;
54
+ http_status?: number;
55
+ http_event_count?: number;
56
+ };
@@ -0,0 +1,158 @@
1
+ /*
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ import { Logger } from "@azure/msal-common";
7
+ import {
8
+ BrowserAuthError,
9
+ BrowserAuthErrorCodes,
10
+ } from "../error/BrowserAuthError";
11
+ import { DatabaseStorage } from "./DatabaseStorage";
12
+ import { IAsyncStorage } from "./IAsyncMemoryStorage";
13
+ import { MemoryStorage } from "./MemoryStorage";
14
+
15
+ /**
16
+ * This class allows MSAL to store artifacts asynchronously using the DatabaseStorage IndexedDB wrapper,
17
+ * backed up with the more volatile MemoryStorage object for cases in which IndexedDB may be unavailable.
18
+ */
19
+ export class AsyncMemoryStorage<T> implements IAsyncStorage<T> {
20
+ private inMemoryCache: MemoryStorage<T>;
21
+ private indexedDBCache: DatabaseStorage<T>;
22
+ private logger: Logger;
23
+ private storeName: string;
24
+
25
+ constructor(logger: Logger, storeName: string) {
26
+ this.inMemoryCache = new MemoryStorage<T>();
27
+ this.indexedDBCache = new DatabaseStorage<T>();
28
+ this.logger = logger;
29
+ this.storeName = storeName;
30
+ }
31
+
32
+ private handleDatabaseAccessError(error: unknown): void {
33
+ if (
34
+ error instanceof BrowserAuthError &&
35
+ error.errorCode === BrowserAuthErrorCodes.databaseUnavailable
36
+ ) {
37
+ this.logger.error(
38
+ "Could not access persistent storage. This may be caused by browser privacy features which block persistent storage in third-party contexts."
39
+ );
40
+ } else {
41
+ throw error;
42
+ }
43
+ }
44
+ /**
45
+ * Get the item matching the given key. Tries in-memory cache first, then in the asynchronous
46
+ * storage object if item isn't found in-memory.
47
+ * @param key
48
+ */
49
+ async getItem(key: string): Promise<T | null> {
50
+ const item = this.inMemoryCache.getItem(key);
51
+ if (!item) {
52
+ try {
53
+ this.logger.verbose(
54
+ "Queried item not found in in-memory cache, now querying persistent storage."
55
+ );
56
+ return await this.indexedDBCache.getItem(key);
57
+ } catch (e) {
58
+ this.handleDatabaseAccessError(e);
59
+ }
60
+ }
61
+ return item;
62
+ }
63
+
64
+ /**
65
+ * Sets the item in the in-memory cache and then tries to set it in the asynchronous
66
+ * storage object with the given key.
67
+ * @param key
68
+ * @param value
69
+ */
70
+ async setItem(key: string, value: T): Promise<void> {
71
+ this.inMemoryCache.setItem(key, value);
72
+ try {
73
+ await this.indexedDBCache.setItem(key, value);
74
+ } catch (e) {
75
+ this.handleDatabaseAccessError(e);
76
+ }
77
+ }
78
+
79
+ /**
80
+ * Removes the item matching the key from the in-memory cache, then tries to remove it from the asynchronous storage object.
81
+ * @param key
82
+ */
83
+ async removeItem(key: string): Promise<void> {
84
+ this.inMemoryCache.removeItem(key);
85
+ try {
86
+ await this.indexedDBCache.removeItem(key);
87
+ } catch (e) {
88
+ this.handleDatabaseAccessError(e);
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Get all the keys from the in-memory cache as an iterable array of strings. If no keys are found, query the keys in the
94
+ * asynchronous storage object.
95
+ */
96
+ async getKeys(): Promise<string[]> {
97
+ const cacheKeys = this.inMemoryCache.getKeys();
98
+ if (cacheKeys.length === 0) {
99
+ try {
100
+ this.logger.verbose(
101
+ "In-memory cache is empty, now querying persistent storage."
102
+ );
103
+ return await this.indexedDBCache.getKeys();
104
+ } catch (e) {
105
+ this.handleDatabaseAccessError(e);
106
+ }
107
+ }
108
+ return cacheKeys;
109
+ }
110
+
111
+ /**
112
+ * Returns true or false if the given key is present in the cache.
113
+ * @param key
114
+ */
115
+ async containsKey(key: string): Promise<boolean> {
116
+ const containsKey = this.inMemoryCache.containsKey(key);
117
+ if (!containsKey) {
118
+ try {
119
+ this.logger.verbose(
120
+ "Key not found in in-memory cache, now querying persistent storage."
121
+ );
122
+ return await this.indexedDBCache.containsKey(key);
123
+ } catch (e) {
124
+ this.handleDatabaseAccessError(e);
125
+ }
126
+ }
127
+ return containsKey;
128
+ }
129
+
130
+ /**
131
+ * Clears in-memory Map
132
+ */
133
+ clearInMemory(): void {
134
+ // InMemory cache is a Map instance, clear is straightforward
135
+ this.logger.verbose(`Deleting in-memory keystore ${this.storeName}`);
136
+ this.inMemoryCache.clear();
137
+ this.logger.verbose(`In-memory keystore ${this.storeName} deleted`);
138
+ }
139
+
140
+ /**
141
+ * Tries to delete the IndexedDB database
142
+ * @returns
143
+ */
144
+ async clearPersistent(): Promise<boolean> {
145
+ try {
146
+ this.logger.verbose("Deleting persistent keystore");
147
+ const dbDeleted = await this.indexedDBCache.deleteDatabase();
148
+ if (dbDeleted) {
149
+ this.logger.verbose("Persistent keystore deleted");
150
+ }
151
+
152
+ return dbDeleted;
153
+ } catch (e) {
154
+ this.handleDatabaseAccessError(e);
155
+ return false;
156
+ }
157
+ }
158
+ }