@azure/msal-browser 3.0.2 → 3.2.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 (280) 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 +22 -18
  4. package/dist/app/IPublicClientApplication.mjs.map +1 -1
  5. package/dist/app/PublicClientApplication.d.ts +20 -6
  6. package/dist/app/PublicClientApplication.d.ts.map +1 -1
  7. package/dist/app/PublicClientApplication.mjs +24 -7
  8. package/dist/app/PublicClientApplication.mjs.map +1 -1
  9. package/dist/broker/nativeBroker/NativeMessageHandler.d.ts +3 -4
  10. package/dist/broker/nativeBroker/NativeMessageHandler.d.ts.map +1 -1
  11. package/dist/broker/nativeBroker/NativeMessageHandler.mjs +17 -16
  12. package/dist/broker/nativeBroker/NativeMessageHandler.mjs.map +1 -1
  13. package/dist/broker/nativeBroker/NativeStatusCodes.d.ts +8 -0
  14. package/dist/broker/nativeBroker/NativeStatusCodes.d.ts.map +1 -0
  15. package/dist/broker/nativeBroker/NativeStatusCodes.mjs +16 -0
  16. package/dist/broker/nativeBroker/NativeStatusCodes.mjs.map +1 -0
  17. package/dist/cache/AsyncMemoryStorage.mjs +4 -3
  18. package/dist/cache/AsyncMemoryStorage.mjs.map +1 -1
  19. package/dist/cache/BrowserCacheManager.d.ts +10 -3
  20. package/dist/cache/BrowserCacheManager.d.ts.map +1 -1
  21. package/dist/cache/BrowserCacheManager.mjs +58 -29
  22. package/dist/cache/BrowserCacheManager.mjs.map +1 -1
  23. package/dist/cache/BrowserStorage.d.ts.map +1 -1
  24. package/dist/cache/BrowserStorage.mjs +7 -9
  25. package/dist/cache/BrowserStorage.mjs.map +1 -1
  26. package/dist/cache/CryptoKeyStore.mjs +1 -1
  27. package/dist/cache/CryptoKeyStore.mjs.map +1 -1
  28. package/dist/cache/DatabaseStorage.d.ts.map +1 -1
  29. package/dist/cache/DatabaseStorage.mjs +9 -8
  30. package/dist/cache/DatabaseStorage.mjs.map +1 -1
  31. package/dist/cache/MemoryStorage.mjs +1 -1
  32. package/dist/cache/MemoryStorage.mjs.map +1 -1
  33. package/dist/cache/TokenCache.d.ts.map +1 -1
  34. package/dist/cache/TokenCache.mjs +30 -30
  35. package/dist/cache/TokenCache.mjs.map +1 -1
  36. package/dist/config/Configuration.d.ts +1 -0
  37. package/dist/config/Configuration.d.ts.map +1 -1
  38. package/dist/config/Configuration.mjs +12 -16
  39. package/dist/config/Configuration.mjs.map +1 -1
  40. package/dist/controllers/ControllerFactory.mjs +1 -1
  41. package/dist/controllers/ControllerFactory.mjs.map +1 -1
  42. package/dist/controllers/IController.d.ts +5 -28
  43. package/dist/controllers/IController.d.ts.map +1 -1
  44. package/dist/controllers/StandardController.d.ts +17 -31
  45. package/dist/controllers/StandardController.d.ts.map +1 -1
  46. package/dist/controllers/StandardController.mjs +78 -83
  47. package/dist/controllers/StandardController.mjs.map +1 -1
  48. package/dist/crypto/BrowserCrypto.d.ts +44 -49
  49. package/dist/crypto/BrowserCrypto.d.ts.map +1 -1
  50. package/dist/crypto/BrowserCrypto.mjs +78 -77
  51. package/dist/crypto/BrowserCrypto.mjs.map +1 -1
  52. package/dist/crypto/CryptoOps.d.ts +1 -10
  53. package/dist/crypto/CryptoOps.d.ts.map +1 -1
  54. package/dist/crypto/CryptoOps.mjs +23 -34
  55. package/dist/crypto/CryptoOps.mjs.map +1 -1
  56. package/dist/crypto/PkceGenerator.d.ts +6 -22
  57. package/dist/crypto/PkceGenerator.d.ts.map +1 -1
  58. package/dist/crypto/PkceGenerator.mjs +48 -49
  59. package/dist/crypto/PkceGenerator.mjs.map +1 -1
  60. package/dist/crypto/SignedHttpRequest.mjs +1 -1
  61. package/dist/crypto/SignedHttpRequest.mjs.map +1 -1
  62. package/dist/encode/Base64Decode.d.ts +6 -19
  63. package/dist/encode/Base64Decode.d.ts.map +1 -1
  64. package/dist/encode/Base64Decode.mjs +31 -68
  65. package/dist/encode/Base64Decode.mjs.map +1 -1
  66. package/dist/encode/Base64Encode.d.ts +15 -27
  67. package/dist/encode/Base64Encode.d.ts.map +1 -1
  68. package/dist/encode/Base64Encode.mjs +36 -75
  69. package/dist/encode/Base64Encode.mjs.map +1 -1
  70. package/dist/error/BrowserAuthError.d.ts +63 -195
  71. package/dist/error/BrowserAuthError.d.ts.map +1 -1
  72. package/dist/error/BrowserAuthError.mjs +159 -381
  73. package/dist/error/BrowserAuthError.mjs.map +1 -1
  74. package/dist/error/BrowserAuthErrorCodes.d.ts +46 -0
  75. package/dist/error/BrowserAuthErrorCodes.d.ts.map +1 -0
  76. package/dist/error/BrowserAuthErrorCodes.mjs +54 -0
  77. package/dist/error/BrowserAuthErrorCodes.mjs.map +1 -0
  78. package/dist/error/BrowserConfigurationAuthError.d.ts +9 -46
  79. package/dist/error/BrowserConfigurationAuthError.d.ts.map +1 -1
  80. package/dist/error/BrowserConfigurationAuthError.mjs +20 -73
  81. package/dist/error/BrowserConfigurationAuthError.mjs.map +1 -1
  82. package/dist/error/BrowserConfigurationAuthErrorCodes.d.ts +4 -0
  83. package/dist/error/BrowserConfigurationAuthErrorCodes.d.ts.map +1 -0
  84. package/dist/error/BrowserConfigurationAuthErrorCodes.mjs +12 -0
  85. package/dist/error/BrowserConfigurationAuthErrorCodes.mjs.map +1 -0
  86. package/dist/error/NativeAuthError.d.ts +17 -45
  87. package/dist/error/NativeAuthError.d.ts.map +1 -1
  88. package/dist/error/NativeAuthError.mjs +49 -79
  89. package/dist/error/NativeAuthError.mjs.map +1 -1
  90. package/dist/error/NativeAuthErrorCodes.d.ts +3 -0
  91. package/dist/error/NativeAuthErrorCodes.d.ts.map +1 -0
  92. package/dist/error/NativeAuthErrorCodes.mjs +11 -0
  93. package/dist/error/NativeAuthErrorCodes.mjs.map +1 -0
  94. package/dist/event/EventHandler.d.ts.map +1 -1
  95. package/dist/event/EventHandler.mjs +3 -2
  96. package/dist/event/EventHandler.mjs.map +1 -1
  97. package/dist/event/EventMessage.mjs +1 -1
  98. package/dist/event/EventMessage.mjs.map +1 -1
  99. package/dist/event/EventType.mjs +1 -1
  100. package/dist/event/EventType.mjs.map +1 -1
  101. package/dist/index.d.ts +6 -6
  102. package/dist/index.d.ts.map +1 -1
  103. package/dist/index.mjs +8 -4
  104. package/dist/index.mjs.map +1 -1
  105. package/dist/interaction_client/BaseInteractionClient.d.ts +2 -1
  106. package/dist/interaction_client/BaseInteractionClient.d.ts.map +1 -1
  107. package/dist/interaction_client/BaseInteractionClient.mjs +7 -8
  108. package/dist/interaction_client/BaseInteractionClient.mjs.map +1 -1
  109. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs +1 -1
  110. package/dist/interaction_client/HybridSpaAuthorizationCodeClient.mjs.map +1 -1
  111. package/dist/interaction_client/NativeInteractionClient.d.ts +4 -10
  112. package/dist/interaction_client/NativeInteractionClient.d.ts.map +1 -1
  113. package/dist/interaction_client/NativeInteractionClient.mjs +32 -38
  114. package/dist/interaction_client/NativeInteractionClient.mjs.map +1 -1
  115. package/dist/interaction_client/PopupClient.d.ts.map +1 -1
  116. package/dist/interaction_client/PopupClient.mjs +16 -15
  117. package/dist/interaction_client/PopupClient.mjs.map +1 -1
  118. package/dist/interaction_client/RedirectClient.d.ts.map +1 -1
  119. package/dist/interaction_client/RedirectClient.mjs +7 -6
  120. package/dist/interaction_client/RedirectClient.mjs.map +1 -1
  121. package/dist/interaction_client/SilentAuthCodeClient.d.ts.map +1 -1
  122. package/dist/interaction_client/SilentAuthCodeClient.mjs +9 -11
  123. package/dist/interaction_client/SilentAuthCodeClient.mjs.map +1 -1
  124. package/dist/interaction_client/SilentCacheClient.d.ts +4 -2
  125. package/dist/interaction_client/SilentCacheClient.d.ts.map +1 -1
  126. package/dist/interaction_client/SilentCacheClient.mjs +19 -25
  127. package/dist/interaction_client/SilentCacheClient.mjs.map +1 -1
  128. package/dist/interaction_client/SilentIframeClient.d.ts.map +1 -1
  129. package/dist/interaction_client/SilentIframeClient.mjs +20 -47
  130. package/dist/interaction_client/SilentIframeClient.mjs.map +1 -1
  131. package/dist/interaction_client/SilentRefreshClient.d.ts.map +1 -1
  132. package/dist/interaction_client/SilentRefreshClient.mjs +11 -20
  133. package/dist/interaction_client/SilentRefreshClient.mjs.map +1 -1
  134. package/dist/interaction_client/StandardInteractionClient.d.ts.map +1 -1
  135. package/dist/interaction_client/StandardInteractionClient.mjs +18 -38
  136. package/dist/interaction_client/StandardInteractionClient.mjs.map +1 -1
  137. package/dist/interaction_handler/InteractionHandler.d.ts.map +1 -1
  138. package/dist/interaction_handler/InteractionHandler.mjs +13 -16
  139. package/dist/interaction_handler/InteractionHandler.mjs.map +1 -1
  140. package/dist/interaction_handler/RedirectHandler.d.ts.map +1 -1
  141. package/dist/interaction_handler/RedirectHandler.mjs +12 -11
  142. package/dist/interaction_handler/RedirectHandler.mjs.map +1 -1
  143. package/dist/interaction_handler/SilentHandler.d.ts.map +1 -1
  144. package/dist/interaction_handler/SilentHandler.mjs +11 -11
  145. package/dist/interaction_handler/SilentHandler.mjs.map +1 -1
  146. package/dist/navigation/NavigationClient.mjs +1 -1
  147. package/dist/navigation/NavigationClient.mjs.map +1 -1
  148. package/dist/network/FetchClient.d.ts.map +1 -1
  149. package/dist/network/FetchClient.mjs +9 -8
  150. package/dist/network/FetchClient.mjs.map +1 -1
  151. package/dist/operatingcontext/BaseOperatingContext.mjs +1 -1
  152. package/dist/operatingcontext/BaseOperatingContext.mjs.map +1 -1
  153. package/dist/operatingcontext/StandardOperatingContext.mjs +1 -1
  154. package/dist/operatingcontext/StandardOperatingContext.mjs.map +1 -1
  155. package/dist/operatingcontext/TeamsAppOperatingContext.mjs +1 -1
  156. package/dist/operatingcontext/TeamsAppOperatingContext.mjs.map +1 -1
  157. package/dist/packageMetadata.d.ts +1 -1
  158. package/dist/packageMetadata.mjs +2 -2
  159. package/dist/packageMetadata.mjs.map +1 -1
  160. package/dist/request/ClearCacheRequest.d.ts +11 -0
  161. package/dist/request/ClearCacheRequest.d.ts.map +1 -0
  162. package/dist/telemetry/BrowserPerformanceClient.d.ts +2 -4
  163. package/dist/telemetry/BrowserPerformanceClient.d.ts.map +1 -1
  164. package/dist/telemetry/BrowserPerformanceClient.mjs +2 -6
  165. package/dist/telemetry/BrowserPerformanceClient.mjs.map +1 -1
  166. package/dist/telemetry/BrowserPerformanceMeasurement.mjs +1 -1
  167. package/dist/telemetry/BrowserPerformanceMeasurement.mjs.map +1 -1
  168. package/dist/utils/BrowserConstants.mjs +1 -1
  169. package/dist/utils/BrowserConstants.mjs.map +1 -1
  170. package/dist/utils/BrowserProtocolUtils.d.ts.map +1 -1
  171. package/dist/utils/BrowserProtocolUtils.mjs +4 -4
  172. package/dist/utils/BrowserProtocolUtils.mjs.map +1 -1
  173. package/dist/utils/BrowserStringUtils.mjs +1 -1
  174. package/dist/utils/BrowserStringUtils.mjs.map +1 -1
  175. package/dist/utils/BrowserUtils.d.ts +0 -9
  176. package/dist/utils/BrowserUtils.d.ts.map +1 -1
  177. package/dist/utils/BrowserUtils.mjs +8 -33
  178. package/dist/utils/BrowserUtils.mjs.map +1 -1
  179. package/lib/msal-browser.cjs +8729 -9566
  180. package/lib/msal-browser.cjs.map +1 -1
  181. package/lib/msal-browser.js +8729 -9566
  182. package/lib/msal-browser.js.map +1 -1
  183. package/lib/msal-browser.min.js +68 -62
  184. package/package.json +7 -5
  185. package/src/app/IPublicClientApplication.ts +236 -0
  186. package/src/app/PublicClientApplication.ts +417 -0
  187. package/src/broker/nativeBroker/NativeMessageHandler.ts +416 -0
  188. package/src/broker/nativeBroker/NativeRequest.ts +52 -0
  189. package/src/broker/nativeBroker/NativeResponse.ts +56 -0
  190. package/src/broker/nativeBroker/NativeStatusCodes.ts +13 -0
  191. package/src/cache/AsyncMemoryStorage.ts +158 -0
  192. package/src/cache/BrowserCacheManager.ts +2009 -0
  193. package/src/cache/BrowserStorage.ts +52 -0
  194. package/src/cache/CryptoKeyStore.ts +63 -0
  195. package/src/cache/DatabaseStorage.ts +287 -0
  196. package/src/cache/IAsyncMemoryStorage.ts +36 -0
  197. package/src/cache/ITokenCache.ts +21 -0
  198. package/src/cache/IWindowStorage.ts +36 -0
  199. package/src/cache/MemoryStorage.ts +42 -0
  200. package/src/cache/TokenCache.ts +484 -0
  201. package/src/config/Configuration.ts +383 -0
  202. package/src/controllers/ControllerFactory.ts +55 -0
  203. package/src/controllers/IController.ts +134 -0
  204. package/src/controllers/StandardController.ts +2170 -0
  205. package/src/crypto/BrowserCrypto.ts +151 -0
  206. package/src/crypto/CryptoOps.ts +235 -0
  207. package/src/crypto/PkceGenerator.ts +115 -0
  208. package/src/crypto/SignedHttpRequest.ts +76 -0
  209. package/src/encode/Base64Decode.ts +46 -0
  210. package/src/encode/Base64Encode.ts +52 -0
  211. package/src/error/BrowserAuthError.ts +352 -0
  212. package/src/error/BrowserAuthErrorCodes.ts +57 -0
  213. package/src/error/BrowserConfigurationAuthError.ts +64 -0
  214. package/src/error/BrowserConfigurationAuthErrorCodes.ts +9 -0
  215. package/src/error/NativeAuthError.ts +113 -0
  216. package/src/error/NativeAuthErrorCodes.ts +7 -0
  217. package/src/event/EventHandler.ts +177 -0
  218. package/src/event/EventMessage.ts +125 -0
  219. package/src/event/EventType.ts +33 -0
  220. package/src/index.ts +157 -0
  221. package/src/interaction_client/BaseInteractionClient.ts +307 -0
  222. package/src/interaction_client/HybridSpaAuthorizationCodeClient.ts +16 -0
  223. package/src/interaction_client/NativeInteractionClient.ts +925 -0
  224. package/src/interaction_client/PopupClient.ts +926 -0
  225. package/src/interaction_client/RedirectClient.ts +616 -0
  226. package/src/interaction_client/SilentAuthCodeClient.ts +165 -0
  227. package/src/interaction_client/SilentCacheClient.ts +125 -0
  228. package/src/interaction_client/SilentIframeClient.ts +301 -0
  229. package/src/interaction_client/SilentRefreshClient.ts +118 -0
  230. package/src/interaction_client/StandardInteractionClient.ts +450 -0
  231. package/src/interaction_handler/InteractionHandler.ts +251 -0
  232. package/src/interaction_handler/RedirectHandler.ts +229 -0
  233. package/src/interaction_handler/SilentHandler.ts +269 -0
  234. package/src/navigation/INavigationClient.ts +23 -0
  235. package/src/navigation/NavigationClient.ts +55 -0
  236. package/src/navigation/NavigationOptions.ts +18 -0
  237. package/src/network/FetchClient.ts +130 -0
  238. package/src/operatingcontext/BaseOperatingContext.ts +82 -0
  239. package/src/operatingcontext/StandardOperatingContext.ts +49 -0
  240. package/src/operatingcontext/TeamsAppOperatingContext.ts +48 -0
  241. package/src/packageMetadata.ts +3 -0
  242. package/src/request/AuthorizationCodeRequest.ts +19 -0
  243. package/src/request/AuthorizationUrlRequest.ts +17 -0
  244. package/src/request/ClearCacheRequest.ts +16 -0
  245. package/src/request/EndSessionPopupRequest.ts +26 -0
  246. package/src/request/EndSessionRequest.ts +23 -0
  247. package/src/request/PopupRequest.ts +51 -0
  248. package/src/request/PopupWindowAttributes.ts +22 -0
  249. package/src/request/RedirectRequest.ts +51 -0
  250. package/src/request/SilentRequest.ts +49 -0
  251. package/src/request/SsoSilentRequest.ts +44 -0
  252. package/src/response/AuthenticationResult.ts +13 -0
  253. package/src/telemetry/BrowserPerformanceClient.ts +226 -0
  254. package/src/telemetry/BrowserPerformanceMeasurement.ts +144 -0
  255. package/src/utils/BrowserConstants.ts +241 -0
  256. package/src/utils/BrowserProtocolUtils.ts +58 -0
  257. package/src/utils/BrowserStringUtils.ts +143 -0
  258. package/src/utils/BrowserUtils.ts +156 -0
  259. package/dist/crypto/GuidGenerator.d.ts +0 -13
  260. package/dist/crypto/GuidGenerator.d.ts.map +0 -1
  261. package/dist/crypto/GuidGenerator.mjs +0 -104
  262. package/dist/crypto/GuidGenerator.mjs.map +0 -1
  263. package/dist/crypto/ISubtleCrypto.d.ts +0 -10
  264. package/dist/crypto/ISubtleCrypto.d.ts.map +0 -1
  265. package/dist/crypto/ModernBrowserCrypto.d.ts +0 -10
  266. package/dist/crypto/ModernBrowserCrypto.d.ts.map +0 -1
  267. package/dist/crypto/ModernBrowserCrypto.mjs +0 -31
  268. package/dist/crypto/ModernBrowserCrypto.mjs.map +0 -1
  269. package/dist/internals.d.ts +0 -23
  270. package/dist/internals.d.ts.map +0 -1
  271. package/dist/internals.mjs +0 -28
  272. package/dist/internals.mjs.map +0 -1
  273. package/dist/network/XhrClient.d.ts +0 -41
  274. package/dist/network/XhrClient.d.ts.map +0 -1
  275. package/dist/network/XhrClient.mjs +0 -125
  276. package/dist/network/XhrClient.mjs.map +0 -1
  277. package/dist/utils/MathUtils.d.ts +0 -12
  278. package/dist/utils/MathUtils.d.ts.map +0 -1
  279. package/dist/utils/MathUtils.mjs +0 -26
  280. package/dist/utils/MathUtils.mjs.map +0 -1
@@ -0,0 +1,416 @@
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
+ createAuthError,
14
+ AuthErrorCodes,
15
+ AuthenticationScheme,
16
+ InProgressPerformanceEvent,
17
+ PerformanceEvents,
18
+ IPerformanceClient,
19
+ } from "@azure/msal-common";
20
+ import {
21
+ NativeExtensionRequest,
22
+ NativeExtensionRequestBody,
23
+ } from "./NativeRequest";
24
+ import { createNativeAuthError } from "../../error/NativeAuthError";
25
+ import {
26
+ createBrowserAuthError,
27
+ BrowserAuthErrorCodes,
28
+ } from "../../error/BrowserAuthError";
29
+ import { BrowserConfiguration } from "../../config/Configuration";
30
+ import { createNewGuid } from "../../crypto/BrowserCrypto";
31
+
32
+ type ResponseResolvers<T> = {
33
+ resolve: (value: T | PromiseLike<T>) => void;
34
+ reject: (
35
+ value: AuthError | Error | PromiseLike<Error> | PromiseLike<AuthError>
36
+ ) => void;
37
+ };
38
+
39
+ export class NativeMessageHandler {
40
+ private extensionId: string | undefined;
41
+ private extensionVersion: string | undefined;
42
+ private logger: Logger;
43
+ private readonly handshakeTimeoutMs: number;
44
+ private timeoutId: number | undefined;
45
+ private resolvers: Map<string, ResponseResolvers<object>>;
46
+ private handshakeResolvers: Map<string, ResponseResolvers<void>>;
47
+ private messageChannel: MessageChannel;
48
+ private readonly windowListener: (event: MessageEvent) => void;
49
+ private readonly performanceClient: IPerformanceClient;
50
+ private readonly handshakeEvent: InProgressPerformanceEvent;
51
+
52
+ constructor(
53
+ logger: Logger,
54
+ handshakeTimeoutMs: number,
55
+ performanceClient: IPerformanceClient,
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
+ }
70
+
71
+ /**
72
+ * Sends a given message to the extension and resolves with the extension response
73
+ * @param body
74
+ */
75
+ async sendMessage(body: NativeExtensionRequestBody): Promise<object> {
76
+ this.logger.trace("NativeMessageHandler - sendMessage called.");
77
+ const req: NativeExtensionRequest = {
78
+ channel: NativeConstants.CHANNEL_ID,
79
+ extensionId: this.extensionId,
80
+ responseId: createNewGuid(),
81
+ body: body,
82
+ };
83
+
84
+ this.logger.trace(
85
+ "NativeMessageHandler - Sending request to browser extension"
86
+ );
87
+ this.logger.tracePii(
88
+ `NativeMessageHandler - Sending request to browser extension: ${JSON.stringify(
89
+ req
90
+ )}`
91
+ );
92
+ this.messageChannel.port1.postMessage(req);
93
+
94
+ return new Promise((resolve, reject) => {
95
+ this.resolvers.set(req.responseId, { resolve, reject });
96
+ });
97
+ }
98
+
99
+ /**
100
+ * Returns an instance of the MessageHandler that has successfully established a connection with an extension
101
+ * @param {Logger} logger
102
+ * @param {number} handshakeTimeoutMs
103
+ * @param {IPerformanceClient} performanceClient
104
+ * @param {ICrypto} crypto
105
+ */
106
+ static async createProvider(
107
+ logger: Logger,
108
+ handshakeTimeoutMs: number,
109
+ performanceClient: IPerformanceClient
110
+ ): Promise<NativeMessageHandler> {
111
+ logger.trace("NativeMessageHandler - createProvider called.");
112
+ try {
113
+ const preferredProvider = new NativeMessageHandler(
114
+ logger,
115
+ handshakeTimeoutMs,
116
+ performanceClient,
117
+ NativeConstants.PREFERRED_EXTENSION_ID
118
+ );
119
+ await preferredProvider.sendHandshakeRequest();
120
+ return preferredProvider;
121
+ } catch (e) {
122
+ // If preferred extension fails for whatever reason, fallback to using any installed extension
123
+ const backupProvider = new NativeMessageHandler(
124
+ logger,
125
+ handshakeTimeoutMs,
126
+ performanceClient
127
+ );
128
+ await backupProvider.sendHandshakeRequest();
129
+ return backupProvider;
130
+ }
131
+ }
132
+
133
+ /**
134
+ * Send handshake request helper.
135
+ */
136
+ private async sendHandshakeRequest(): Promise<void> {
137
+ this.logger.trace(
138
+ "NativeMessageHandler - sendHandshakeRequest called."
139
+ );
140
+ // Register this event listener before sending handshake
141
+ window.addEventListener("message", this.windowListener, false); // false is important, because content script message processing should work first
142
+
143
+ const req: NativeExtensionRequest = {
144
+ channel: NativeConstants.CHANNEL_ID,
145
+ extensionId: this.extensionId,
146
+ responseId: createNewGuid(),
147
+ body: {
148
+ method: NativeExtensionMethod.HandshakeRequest,
149
+ },
150
+ };
151
+ this.handshakeEvent.add({
152
+ extensionId: this.extensionId,
153
+ extensionHandshakeTimeoutMs: this.handshakeTimeoutMs,
154
+ });
155
+
156
+ this.messageChannel.port1.onmessage = (event) => {
157
+ this.onChannelMessage(event);
158
+ };
159
+
160
+ window.postMessage(req, window.origin, [this.messageChannel.port2]);
161
+
162
+ return new Promise((resolve, reject) => {
163
+ this.handshakeResolvers.set(req.responseId, { resolve, reject });
164
+ this.timeoutId = window.setTimeout(() => {
165
+ /*
166
+ * Throw an error if neither HandshakeResponse nor original Handshake request are received in a reasonable timeframe.
167
+ * This typically suggests an event handler stopped propagation of the Handshake request but did not respond to it on the MessageChannel port
168
+ */
169
+ window.removeEventListener(
170
+ "message",
171
+ this.windowListener,
172
+ false
173
+ );
174
+ this.messageChannel.port1.close();
175
+ this.messageChannel.port2.close();
176
+ this.handshakeEvent.end({
177
+ extensionHandshakeTimedOut: true,
178
+ success: false,
179
+ });
180
+ reject(
181
+ createBrowserAuthError(
182
+ BrowserAuthErrorCodes.nativeHandshakeTimeout
183
+ )
184
+ );
185
+ this.handshakeResolvers.delete(req.responseId);
186
+ }, this.handshakeTimeoutMs); // Use a reasonable timeout in milliseconds here
187
+ });
188
+ }
189
+
190
+ /**
191
+ * Invoked when a message is posted to the window. If a handshake request is received it means the extension is not installed.
192
+ * @param event
193
+ */
194
+ private onWindowMessage(event: MessageEvent): void {
195
+ this.logger.trace("NativeMessageHandler - onWindowMessage called");
196
+ // We only accept messages from ourselves
197
+ if (event.source !== window) {
198
+ return;
199
+ }
200
+
201
+ const request = event.data;
202
+
203
+ if (
204
+ !request.channel ||
205
+ request.channel !== NativeConstants.CHANNEL_ID
206
+ ) {
207
+ return;
208
+ }
209
+
210
+ if (request.extensionId && request.extensionId !== this.extensionId) {
211
+ return;
212
+ }
213
+
214
+ if (request.body.method === NativeExtensionMethod.HandshakeRequest) {
215
+ const handshakeResolver = this.handshakeResolvers.get(
216
+ request.responseId
217
+ );
218
+ /*
219
+ * Filter out responses with no matched resolvers sooner to keep channel ports open while waiting for
220
+ * the proper response.
221
+ */
222
+ if (!handshakeResolver) {
223
+ this.logger.trace(
224
+ `NativeMessageHandler.onWindowMessage - resolver can't be found for request ${request.responseId}`
225
+ );
226
+ return;
227
+ }
228
+
229
+ // If we receive this message back it means no extension intercepted the request, meaning no extension supporting handshake protocol is installed
230
+ this.logger.verbose(
231
+ request.extensionId
232
+ ? `Extension with id: ${request.extensionId} not installed`
233
+ : "No extension installed"
234
+ );
235
+ clearTimeout(this.timeoutId);
236
+ this.messageChannel.port1.close();
237
+ this.messageChannel.port2.close();
238
+ window.removeEventListener("message", this.windowListener, false);
239
+ this.handshakeEvent.end({
240
+ success: false,
241
+ extensionInstalled: false,
242
+ });
243
+ handshakeResolver.reject(
244
+ createBrowserAuthError(
245
+ BrowserAuthErrorCodes.nativeExtensionNotInstalled
246
+ )
247
+ );
248
+ }
249
+ }
250
+
251
+ /**
252
+ * Invoked when a message is received from the extension on the MessageChannel port
253
+ * @param event
254
+ */
255
+ private onChannelMessage(event: MessageEvent): void {
256
+ this.logger.trace("NativeMessageHandler - onChannelMessage called.");
257
+ const request = event.data;
258
+
259
+ const resolver = this.resolvers.get(request.responseId);
260
+ const handshakeResolver = this.handshakeResolvers.get(
261
+ request.responseId
262
+ );
263
+
264
+ try {
265
+ const method = request.body.method;
266
+
267
+ if (method === NativeExtensionMethod.Response) {
268
+ if (!resolver) {
269
+ return;
270
+ }
271
+ const response = request.body.response;
272
+ this.logger.trace(
273
+ "NativeMessageHandler - Received response from browser extension"
274
+ );
275
+ this.logger.tracePii(
276
+ `NativeMessageHandler - Received response from browser extension: ${JSON.stringify(
277
+ response
278
+ )}`
279
+ );
280
+ if (response.status !== "Success") {
281
+ resolver.reject(
282
+ createNativeAuthError(
283
+ response.code,
284
+ response.description,
285
+ response.ext
286
+ )
287
+ );
288
+ } else if (response.result) {
289
+ if (
290
+ response.result["code"] &&
291
+ response.result["description"]
292
+ ) {
293
+ resolver.reject(
294
+ createNativeAuthError(
295
+ response.result["code"],
296
+ response.result["description"],
297
+ response.result["ext"]
298
+ )
299
+ );
300
+ } else {
301
+ resolver.resolve(response.result);
302
+ }
303
+ } else {
304
+ throw createAuthError(
305
+ AuthErrorCodes.unexpectedError,
306
+ "Event does not contain result."
307
+ );
308
+ }
309
+ this.resolvers.delete(request.responseId);
310
+ } else if (method === NativeExtensionMethod.HandshakeResponse) {
311
+ if (!handshakeResolver) {
312
+ this.logger.trace(
313
+ `NativeMessageHandler.onChannelMessage - resolver can't be found for request ${request.responseId}`
314
+ );
315
+ return;
316
+ }
317
+ clearTimeout(this.timeoutId); // Clear setTimeout
318
+ window.removeEventListener(
319
+ "message",
320
+ this.windowListener,
321
+ false
322
+ ); // Remove 'No extension' listener
323
+ this.extensionId = request.extensionId;
324
+ this.extensionVersion = request.body.version;
325
+ this.logger.verbose(
326
+ `NativeMessageHandler - Received HandshakeResponse from extension: ${this.extensionId}`
327
+ );
328
+ this.handshakeEvent.end({
329
+ extensionInstalled: true,
330
+ success: true,
331
+ });
332
+
333
+ handshakeResolver.resolve();
334
+ this.handshakeResolvers.delete(request.responseId);
335
+ }
336
+ // Do nothing if method is not Response or HandshakeResponse
337
+ } catch (err) {
338
+ this.logger.error("Error parsing response from WAM Extension");
339
+ this.logger.errorPii(
340
+ `Error parsing response from WAM Extension: ${err as string}`
341
+ );
342
+ this.logger.errorPii(`Unable to parse ${event}`);
343
+
344
+ if (resolver) {
345
+ resolver.reject(err as AuthError);
346
+ } else if (handshakeResolver) {
347
+ handshakeResolver.reject(err as AuthError);
348
+ }
349
+ }
350
+ }
351
+
352
+ /**
353
+ * Returns the Id for the browser extension this handler is communicating with
354
+ * @returns
355
+ */
356
+ getExtensionId(): string | undefined {
357
+ return this.extensionId;
358
+ }
359
+
360
+ /**
361
+ * Returns the version for the browser extension this handler is communicating with
362
+ * @returns
363
+ */
364
+ getExtensionVersion(): string | undefined {
365
+ return this.extensionVersion;
366
+ }
367
+
368
+ /**
369
+ * Returns boolean indicating whether or not the request should attempt to use native broker
370
+ * @param logger
371
+ * @param config
372
+ * @param nativeExtensionProvider
373
+ * @param authenticationScheme
374
+ */
375
+ static isNativeAvailable(
376
+ config: BrowserConfiguration,
377
+ logger: Logger,
378
+ nativeExtensionProvider?: NativeMessageHandler,
379
+ authenticationScheme?: AuthenticationScheme
380
+ ): boolean {
381
+ logger.trace("isNativeAvailable called");
382
+ if (!config.system.allowNativeBroker) {
383
+ logger.trace(
384
+ "isNativeAvailable: allowNativeBroker is not enabled, returning false"
385
+ );
386
+ // Developer disabled WAM
387
+ return false;
388
+ }
389
+
390
+ if (!nativeExtensionProvider) {
391
+ logger.trace(
392
+ "isNativeAvailable: WAM extension provider is not initialized, returning false"
393
+ );
394
+ // Extension is not available
395
+ return false;
396
+ }
397
+
398
+ if (authenticationScheme) {
399
+ switch (authenticationScheme) {
400
+ case AuthenticationScheme.BEARER:
401
+ case AuthenticationScheme.POP:
402
+ logger.trace(
403
+ "isNativeAvailable: authenticationScheme is supported, returning true"
404
+ );
405
+ return true;
406
+ default:
407
+ logger.trace(
408
+ "isNativeAvailable: authenticationScheme is not supported, returning false"
409
+ );
410
+ return false;
411
+ }
412
+ }
413
+
414
+ return true;
415
+ }
416
+ }
@@ -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,13 @@
1
+ /*
2
+ * Copyright (c) Microsoft Corporation. All rights reserved.
3
+ * Licensed under the MIT License.
4
+ */
5
+
6
+ // Status Codes that can be thrown by WAM
7
+ export const USER_INTERACTION_REQUIRED = "USER_INTERACTION_REQUIRED";
8
+ export const USER_CANCEL = "USER_CANCEL";
9
+ export const NO_NETWORK = "NO_NETWORK";
10
+ export const TRANSIENT_ERROR = "TRANSIENT_ERROR";
11
+ export const PERSISTENT_ERROR = "PERSISTENT_ERROR";
12
+ export const DISABLED = "DISABLED";
13
+ export const ACCOUNT_UNAVAILABLE = "ACCOUNT_UNAVAILABLE";
@@ -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
+ }