@azure/msal-react 1.2.0 → 1.3.2

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.
@@ -3,4 +3,7 @@ import { IPublicClientApplication } from "@azure/msal-browser";
3
3
  export declare type MsalProviderProps = PropsWithChildren<{
4
4
  instance: IPublicClientApplication;
5
5
  }>;
6
+ /**
7
+ * MSAL context provider component. This must be rendered above any other components that use MSAL.
8
+ */
6
9
  export declare function MsalProvider({ instance, children }: MsalProviderProps): React.ReactElement;
@@ -0,0 +1,16 @@
1
+ import { AuthError } from "@azure/msal-browser";
2
+ export declare const ReactAuthErrorMessage: {
3
+ invalidInteractionType: {
4
+ code: string;
5
+ desc: string;
6
+ };
7
+ unableToFallbackToInteraction: {
8
+ code: string;
9
+ desc: string;
10
+ };
11
+ };
12
+ export declare class ReactAuthError extends AuthError {
13
+ constructor(errorCode: string, errorMessage?: string);
14
+ static createInvalidInteractionTypeError(): ReactAuthError;
15
+ static createUnableToFallbackToInteractionError(): ReactAuthError;
16
+ }
@@ -4,4 +4,4 @@ import { AccountIdentifiers } from "../types/AccountIdentifiers";
4
4
  * Given 1 or more accountIdentifiers, returns the Account object if the user is signed-in
5
5
  * @param accountIdentifiers
6
6
  */
7
- export declare function useAccount(accountIdentifiers: AccountIdentifiers): AccountInfo | null;
7
+ export declare function useAccount(accountIdentifiers?: AccountIdentifiers): AccountInfo | null;
@@ -1,13 +1,15 @@
1
- import { PopupRequest, RedirectRequest, SsoSilentRequest, InteractionType, AuthenticationResult, AuthError } from "@azure/msal-browser";
1
+ import { PopupRequest, RedirectRequest, SsoSilentRequest, InteractionType, AuthenticationResult, AuthError, SilentRequest } from "@azure/msal-browser";
2
2
  import { AccountIdentifiers } from "../types/AccountIdentifiers";
3
3
  export declare type MsalAuthenticationResult = {
4
- login: Function;
4
+ login: (callbackInteractionType?: InteractionType | undefined, callbackRequest?: PopupRequest | RedirectRequest | SilentRequest) => Promise<AuthenticationResult | null>;
5
+ acquireToken: (callbackInteractionType?: InteractionType | undefined, callbackRequest?: SilentRequest | undefined) => Promise<AuthenticationResult | null>;
5
6
  result: AuthenticationResult | null;
6
7
  error: AuthError | null;
7
8
  };
8
9
  /**
9
- * Invokes a login call if a user is not currently signed-in. Failed logins can be retried using the login callback returned.
10
- * Optionally provide a request object to be used in the login call.
10
+ * If a user is not currently signed in this hook invokes a login. Failed logins can be retried using the login callback returned.
11
+ * If a user is currently signed in this hook attempts to acquire a token. Subsequent token requests can use the acquireToken callback returned.
12
+ * Optionally provide a request object to be used in the login/acquireToken call.
11
13
  * Optionally provide a specific user that should be logged in.
12
14
  * @param interactionType
13
15
  * @param authenticationRequest
@@ -60,75 +60,130 @@ function accountArraysAreEqual(arrayA, arrayB) {
60
60
  return elementA.homeAccountId === elementB.homeAccountId && elementA.localAccountId === elementB.localAccountId && elementA.username === elementB.username;
61
61
  });
62
62
  }
63
+ function getAccountByIdentifiers(allAccounts, accountIdentifiers) {
64
+ if (allAccounts.length > 0 && (accountIdentifiers.homeAccountId || accountIdentifiers.localAccountId || accountIdentifiers.username)) {
65
+ const matchedAccounts = allAccounts.filter(accountObj => {
66
+ if (accountIdentifiers.username && accountIdentifiers.username.toLowerCase() !== accountObj.username.toLowerCase()) {
67
+ return false;
68
+ }
69
+
70
+ if (accountIdentifiers.homeAccountId && accountIdentifiers.homeAccountId.toLowerCase() !== accountObj.homeAccountId.toLowerCase()) {
71
+ return false;
72
+ }
73
+
74
+ if (accountIdentifiers.localAccountId && accountIdentifiers.localAccountId.toLowerCase() !== accountObj.localAccountId.toLowerCase()) {
75
+ return false;
76
+ }
77
+
78
+ return true;
79
+ });
80
+ return matchedAccounts[0] || null;
81
+ } else {
82
+ return null;
83
+ }
84
+ }
63
85
 
64
86
  /* eslint-disable header/header */
65
87
  const name = "@azure/msal-react";
66
- const version = "1.2.0";
88
+ const version = "1.3.2";
67
89
 
68
90
  /*
69
91
  * Copyright (c) Microsoft Corporation. All rights reserved.
70
92
  * Licensed under the MIT License.
71
93
  */
72
- function MsalProvider({
73
- instance,
74
- children
75
- }) {
76
- React.useEffect(() => {
77
- instance.initializeWrapperLibrary(msalBrowser.WrapperSKU.React, version);
78
- }, [instance]); // Create a logger instance for msal-react with the same options as PublicClientApplication
94
+ var MsalProviderActionType;
79
95
 
80
- const logger = React.useMemo(() => {
81
- return instance.getLogger().clone(name, version);
82
- }, [instance]); // State hook to store accounts
96
+ (function (MsalProviderActionType) {
97
+ MsalProviderActionType["UNBLOCK_INPROGRESS"] = "UNBLOCK_INPROGRESS";
98
+ MsalProviderActionType["EVENT"] = "EVENT";
99
+ })(MsalProviderActionType || (MsalProviderActionType = {}));
100
+ /**
101
+ * Returns the next inProgress and accounts state based on event message
102
+ * @param previousState
103
+ * @param action
104
+ */
83
105
 
84
- const [accounts, setAccounts] = React.useState([]); // State hook to store in progress value
85
106
 
86
- const [inProgress, setInProgress] = React.useState(msalBrowser.InteractionStatus.Startup); // Mutable object used in the event callback
107
+ const reducer = (previousState, action) => {
108
+ const {
109
+ type,
110
+ payload
111
+ } = action;
112
+ let newAccounts = previousState.accounts;
113
+ let newInProgress = previousState.inProgress;
114
+
115
+ switch (type) {
116
+ case MsalProviderActionType.UNBLOCK_INPROGRESS:
117
+ if (previousState.inProgress === msalBrowser.InteractionStatus.Startup) {
118
+ newInProgress = msalBrowser.InteractionStatus.None;
119
+ payload.logger.info("MsalProvider - handleRedirectPromise resolved, setting inProgress to 'none'");
120
+ }
87
121
 
88
- const inProgressRef = React.useRef(inProgress);
89
- React.useEffect(() => {
90
- const callbackId = instance.addEventCallback(message => {
91
- switch (message.eventType) {
92
- case msalBrowser.EventType.ACCOUNT_ADDED:
93
- case msalBrowser.EventType.ACCOUNT_REMOVED:
94
- case msalBrowser.EventType.LOGIN_SUCCESS:
95
- case msalBrowser.EventType.SSO_SILENT_SUCCESS:
96
- case msalBrowser.EventType.HANDLE_REDIRECT_END:
97
- case msalBrowser.EventType.LOGIN_FAILURE:
98
- case msalBrowser.EventType.SSO_SILENT_FAILURE:
99
- case msalBrowser.EventType.LOGOUT_END:
100
- case msalBrowser.EventType.ACQUIRE_TOKEN_SUCCESS:
101
- case msalBrowser.EventType.ACQUIRE_TOKEN_FAILURE:
102
- const currentAccounts = instance.getAllAccounts();
103
-
104
- if (!accountArraysAreEqual(currentAccounts, accounts)) {
105
- logger.info("MsalProvider - updating account state");
106
- setAccounts(currentAccounts);
107
- } else {
108
- logger.info("MsalProvider - no account changes");
109
- }
122
+ break;
110
123
 
111
- break;
112
- }
113
- });
114
- logger.verbose(`MsalProvider - Registered event callback with id: ${callbackId}`);
115
- return () => {
116
- // Remove callback when component unmounts or accounts change
117
- if (callbackId) {
118
- logger.verbose(`MsalProvider - Removing event callback ${callbackId}`);
119
- instance.removeEventCallback(callbackId);
124
+ case MsalProviderActionType.EVENT:
125
+ const message = payload.message;
126
+ const status = msalBrowser.EventMessageUtils.getInteractionStatusFromEvent(message, previousState.inProgress);
127
+
128
+ if (status) {
129
+ payload.logger.info(`MsalProvider - ${message.eventType} results in setting inProgress from ${previousState.inProgress} to ${status}`);
130
+ newInProgress = status;
120
131
  }
132
+
133
+ break;
134
+
135
+ default:
136
+ throw new Error(`Unknown action type: ${type}`);
137
+ }
138
+
139
+ const currentAccounts = payload.instance.getAllAccounts();
140
+
141
+ if (!accountArraysAreEqual(currentAccounts, previousState.accounts)) {
142
+ payload.logger.info("MsalProvider - updating account state");
143
+ newAccounts = currentAccounts;
144
+ } else {
145
+ payload.logger.verbose("MsalProvider - no account changes");
146
+ }
147
+
148
+ return { ...previousState,
149
+ inProgress: newInProgress,
150
+ accounts: newAccounts
151
+ };
152
+ };
153
+ /**
154
+ * MSAL context provider component. This must be rendered above any other components that use MSAL.
155
+ */
156
+
157
+
158
+ function MsalProvider(_ref) {
159
+ let {
160
+ instance,
161
+ children
162
+ } = _ref;
163
+ React.useEffect(() => {
164
+ instance.initializeWrapperLibrary(msalBrowser.WrapperSKU.React, version);
165
+ }, [instance]); // Create a logger instance for msal-react with the same options as PublicClientApplication
166
+
167
+ const logger = React.useMemo(() => {
168
+ return instance.getLogger().clone(name, version);
169
+ }, [instance]);
170
+ const [state, updateState] = React.useReducer(reducer, undefined, () => {
171
+ // Lazy initialization of the initial state
172
+ return {
173
+ inProgress: msalBrowser.InteractionStatus.Startup,
174
+ accounts: instance.getAllAccounts()
121
175
  };
122
- }, [instance, accounts, logger]);
176
+ });
123
177
  React.useEffect(() => {
124
178
  const callbackId = instance.addEventCallback(message => {
125
- const status = msalBrowser.EventMessageUtils.getInteractionStatusFromEvent(message, inProgressRef.current);
126
-
127
- if (status !== null) {
128
- logger.info(`MsalProvider - ${message.eventType} results in setting inProgress from ${inProgressRef.current} to ${status}`);
129
- inProgressRef.current = status;
130
- setInProgress(status);
131
- }
179
+ updateState({
180
+ payload: {
181
+ instance,
182
+ logger,
183
+ message
184
+ },
185
+ type: MsalProviderActionType.EVENT
186
+ });
132
187
  });
133
188
  logger.verbose(`MsalProvider - Registered event callback with id: ${callbackId}`);
134
189
  instance.handleRedirectPromise().catch(() => {
@@ -139,12 +194,16 @@ function MsalProvider({
139
194
  * If handleRedirectPromise returns a cached promise the necessary events may not be fired
140
195
  * This is a fallback to prevent inProgress from getting stuck in 'startup'
141
196
  */
142
- if (inProgressRef.current === msalBrowser.InteractionStatus.Startup) {
143
- inProgressRef.current = msalBrowser.InteractionStatus.None;
144
- setInProgress(msalBrowser.InteractionStatus.None);
145
- }
197
+ updateState({
198
+ payload: {
199
+ instance,
200
+ logger
201
+ },
202
+ type: MsalProviderActionType.UNBLOCK_INPROGRESS
203
+ });
146
204
  });
147
205
  return () => {
206
+ // Remove callback when component unmounts or accounts change
148
207
  if (callbackId) {
149
208
  logger.verbose(`MsalProvider - Removing event callback ${callbackId}`);
150
209
  instance.removeEventCallback(callbackId);
@@ -153,8 +212,8 @@ function MsalProvider({
153
212
  }, [instance, logger]);
154
213
  const contextValue = {
155
214
  instance,
156
- inProgress,
157
- accounts,
215
+ inProgress: state.inProgress,
216
+ accounts: state.accounts,
158
217
  logger
159
218
  };
160
219
  return React__default.createElement(MsalContext.Provider, {
@@ -177,61 +236,9 @@ const useMsal = () => React.useContext(MsalContext);
177
236
  * Licensed under the MIT License.
178
237
  */
179
238
 
180
- function getAccount(instance, accountIdentifiers) {
181
- const allAccounts = instance.getAllAccounts();
182
-
183
- if (allAccounts.length > 0 && (accountIdentifiers.homeAccountId || accountIdentifiers.localAccountId || accountIdentifiers.username)) {
184
- const matchedAccounts = allAccounts.filter(accountObj => {
185
- if (accountIdentifiers.username && accountIdentifiers.username.toLowerCase() !== accountObj.username.toLowerCase()) {
186
- return false;
187
- }
188
-
189
- if (accountIdentifiers.homeAccountId && accountIdentifiers.homeAccountId.toLowerCase() !== accountObj.homeAccountId.toLowerCase()) {
190
- return false;
191
- }
192
-
193
- if (accountIdentifiers.localAccountId && accountIdentifiers.localAccountId.toLowerCase() !== accountObj.localAccountId.toLowerCase()) {
194
- return false;
195
- }
196
-
197
- return true;
198
- });
199
- return matchedAccounts[0] || null;
200
- } else {
201
- return null;
202
- }
203
- }
204
- /**
205
- * Given 1 or more accountIdentifiers, returns the Account object if the user is signed-in
206
- * @param accountIdentifiers
207
- */
208
-
209
-
210
- function useAccount(accountIdentifiers) {
211
- const {
212
- instance,
213
- inProgress
214
- } = useMsal();
215
- const initialStateValue = inProgress === msalBrowser.InteractionStatus.Startup ? null : getAccount(instance, accountIdentifiers);
216
- const [account, setAccount] = React.useState(initialStateValue);
217
- React.useEffect(() => {
218
- const currentAccount = getAccount(instance, accountIdentifiers);
219
-
220
- if (!msalBrowser.AccountEntity.accountInfoIsEqual(account, currentAccount, true)) {
221
- setAccount(currentAccount);
222
- }
223
- }, [inProgress, accountIdentifiers, instance, account]);
224
- return account;
225
- }
226
-
227
- /*
228
- * Copyright (c) Microsoft Corporation. All rights reserved.
229
- * Licensed under the MIT License.
230
- */
231
-
232
- function isAuthenticated(allAccounts, account, matchAccount) {
239
+ function isAuthenticated(allAccounts, matchAccount) {
233
240
  if (matchAccount && (matchAccount.username || matchAccount.homeAccountId || matchAccount.localAccountId)) {
234
- return !!account;
241
+ return !!getAccountByIdentifiers(allAccounts, matchAccount);
235
242
  }
236
243
 
237
244
  return allAccounts.length > 0;
@@ -244,15 +251,12 @@ function isAuthenticated(allAccounts, account, matchAccount) {
244
251
 
245
252
  function useIsAuthenticated(matchAccount) {
246
253
  const {
247
- accounts: allAccounts,
248
- inProgress
254
+ accounts: allAccounts
249
255
  } = useMsal();
250
- const account = useAccount(matchAccount || {});
251
- const initialStateValue = inProgress === msalBrowser.InteractionStatus.Startup ? false : isAuthenticated(allAccounts, account, matchAccount);
252
- const [hasAuthenticated, setHasAuthenticated] = React.useState(initialStateValue);
256
+ const [hasAuthenticated, setHasAuthenticated] = React.useState(() => isAuthenticated(allAccounts, matchAccount));
253
257
  React.useEffect(() => {
254
- setHasAuthenticated(isAuthenticated(allAccounts, account, matchAccount));
255
- }, [allAccounts, account, matchAccount]);
258
+ setHasAuthenticated(isAuthenticated(allAccounts, matchAccount));
259
+ }, [allAccounts, matchAccount]);
256
260
  return hasAuthenticated;
257
261
  }
258
262
 
@@ -265,12 +269,13 @@ function useIsAuthenticated(matchAccount) {
265
269
  * @param props
266
270
  */
267
271
 
268
- function AuthenticatedTemplate({
269
- username,
270
- homeAccountId,
271
- localAccountId,
272
- children
273
- }) {
272
+ function AuthenticatedTemplate(_ref) {
273
+ let {
274
+ username,
275
+ homeAccountId,
276
+ localAccountId,
277
+ children
278
+ } = _ref;
274
279
  const context = useMsal();
275
280
  const accountIdentifier = React.useMemo(() => {
276
281
  return {
@@ -297,12 +302,13 @@ function AuthenticatedTemplate({
297
302
  * @param props
298
303
  */
299
304
 
300
- function UnauthenticatedTemplate({
301
- username,
302
- homeAccountId,
303
- localAccountId,
304
- children
305
- }) {
305
+ function UnauthenticatedTemplate(_ref) {
306
+ let {
307
+ username,
308
+ homeAccountId,
309
+ localAccountId,
310
+ children
311
+ } = _ref;
306
312
  const context = useMsal();
307
313
  const accountIdentifier = React.useMemo(() => {
308
314
  return {
@@ -320,13 +326,86 @@ function UnauthenticatedTemplate({
320
326
  return null;
321
327
  }
322
328
 
329
+ /*
330
+ * Copyright (c) Microsoft Corporation. All rights reserved.
331
+ * Licensed under the MIT License.
332
+ */
333
+
334
+ function getAccount(instance, accountIdentifiers) {
335
+ if (!accountIdentifiers || !accountIdentifiers.homeAccountId && !accountIdentifiers.localAccountId && !accountIdentifiers.username) {
336
+ // If no account identifiers are provided, return active account
337
+ return instance.getActiveAccount();
338
+ }
339
+
340
+ return getAccountByIdentifiers(instance.getAllAccounts(), accountIdentifiers);
341
+ }
342
+ /**
343
+ * Given 1 or more accountIdentifiers, returns the Account object if the user is signed-in
344
+ * @param accountIdentifiers
345
+ */
346
+
347
+
348
+ function useAccount(accountIdentifiers) {
349
+ const {
350
+ instance,
351
+ inProgress,
352
+ logger
353
+ } = useMsal();
354
+ const [account, setAccount] = React.useState(() => getAccount(instance, accountIdentifiers));
355
+ React.useEffect(() => {
356
+ setAccount(currentAccount => {
357
+ const nextAccount = getAccount(instance, accountIdentifiers);
358
+
359
+ if (!msalBrowser.AccountEntity.accountInfoIsEqual(currentAccount, nextAccount, true)) {
360
+ logger.info("useAccount - Updating account");
361
+ return nextAccount;
362
+ }
363
+
364
+ return currentAccount;
365
+ });
366
+ }, [inProgress, accountIdentifiers, instance, logger]);
367
+ return account;
368
+ }
369
+
370
+ /*
371
+ * Copyright (c) Microsoft Corporation. All rights reserved.
372
+ * Licensed under the MIT License.
373
+ */
374
+ const ReactAuthErrorMessage = {
375
+ invalidInteractionType: {
376
+ code: "invalid_interaction_type",
377
+ desc: "The provided interaction type is invalid."
378
+ },
379
+ unableToFallbackToInteraction: {
380
+ code: "unable_to_fallback_to_interaction",
381
+ desc: "Interaction is required but another interaction is already in progress. Please try again when the current interaction is complete."
382
+ }
383
+ };
384
+ class ReactAuthError extends msalBrowser.AuthError {
385
+ constructor(errorCode, errorMessage) {
386
+ super(errorCode, errorMessage);
387
+ Object.setPrototypeOf(this, ReactAuthError.prototype);
388
+ this.name = "ReactAuthError";
389
+ }
390
+
391
+ static createInvalidInteractionTypeError() {
392
+ return new ReactAuthError(ReactAuthErrorMessage.invalidInteractionType.code, ReactAuthErrorMessage.invalidInteractionType.desc);
393
+ }
394
+
395
+ static createUnableToFallbackToInteractionError() {
396
+ return new ReactAuthError(ReactAuthErrorMessage.unableToFallbackToInteraction.code, ReactAuthErrorMessage.unableToFallbackToInteraction.desc);
397
+ }
398
+
399
+ }
400
+
323
401
  /*
324
402
  * Copyright (c) Microsoft Corporation. All rights reserved.
325
403
  * Licensed under the MIT License.
326
404
  */
327
405
  /**
328
- * Invokes a login call if a user is not currently signed-in. Failed logins can be retried using the login callback returned.
329
- * Optionally provide a request object to be used in the login call.
406
+ * If a user is not currently signed in this hook invokes a login. Failed logins can be retried using the login callback returned.
407
+ * If a user is currently signed in this hook attempts to acquire a token. Subsequent token requests can use the acquireToken callback returned.
408
+ * Optionally provide a request object to be used in the login/acquireToken call.
330
409
  * Optionally provide a specific user that should be logged in.
331
410
  * @param interactionType
332
411
  * @param authenticationRequest
@@ -340,8 +419,28 @@ function useMsalAuthentication(interactionType, authenticationRequest, accountId
340
419
  logger
341
420
  } = useMsal();
342
421
  const isAuthenticated = useIsAuthenticated(accountIdentifiers);
343
- const [[result, error], setResponse] = React.useState([null, null]);
344
- const [hasBeenCalled, setHasBeenCalled] = React.useState(false);
422
+ const account = useAccount(accountIdentifiers);
423
+ const [[result, error], setResponse] = React.useState([null, null]); // Boolean used to check if interaction is in progress in acquireTokenSilent fallback. Use Ref instead of state to prevent acquireToken function from being regenerated on each change to interactionInProgress value
424
+
425
+ const interactionInProgress = React.useRef(inProgress !== msalBrowser.InteractionStatus.None);
426
+ React.useEffect(() => {
427
+ interactionInProgress.current = inProgress !== msalBrowser.InteractionStatus.None;
428
+ }, [inProgress]); // Flag used to control when the hook calls login/acquireToken
429
+
430
+ const shouldAcquireToken = React.useRef(true);
431
+ React.useEffect(() => {
432
+ if (!!error) {
433
+ // Errors should be handled by consuming component
434
+ shouldAcquireToken.current = false;
435
+ return;
436
+ }
437
+
438
+ if (!!result) {
439
+ // Token has already been acquired, consuming component/application is responsible for renewing
440
+ shouldAcquireToken.current = false;
441
+ return;
442
+ }
443
+ }, [error, result]);
345
444
  const login = React.useCallback(async (callbackInteractionType, callbackRequest) => {
346
445
  const loginType = callbackInteractionType || interactionType;
347
446
  const loginRequest = callbackRequest || authenticationRequest;
@@ -361,9 +460,59 @@ function useMsalAuthentication(interactionType, authenticationRequest, accountId
361
460
  return instance.ssoSilent(loginRequest);
362
461
 
363
462
  default:
364
- throw "Invalid interaction type provided.";
463
+ throw ReactAuthError.createInvalidInteractionTypeError();
365
464
  }
366
465
  }, [instance, interactionType, authenticationRequest, logger]);
466
+ const acquireToken = React.useCallback(async (callbackInteractionType, callbackRequest) => {
467
+ const fallbackInteractionType = callbackInteractionType || interactionType;
468
+ let tokenRequest;
469
+
470
+ if (callbackRequest) {
471
+ logger.trace("useMsalAuthentication - acquireToken - Using request provided in the callback");
472
+ tokenRequest = { ...callbackRequest
473
+ };
474
+ } else if (authenticationRequest) {
475
+ logger.trace("useMsalAuthentication - acquireToken - Using request provided in the hook");
476
+ tokenRequest = { ...authenticationRequest,
477
+ scopes: authenticationRequest.scopes || msalBrowser.OIDC_DEFAULT_SCOPES
478
+ };
479
+ } else {
480
+ logger.trace("useMsalAuthentication - acquireToken - No request object provided, using default request.");
481
+ tokenRequest = {
482
+ scopes: msalBrowser.OIDC_DEFAULT_SCOPES
483
+ };
484
+ }
485
+
486
+ if (!tokenRequest.account && account) {
487
+ logger.trace("useMsalAuthentication - acquireToken - Attaching account to request");
488
+ tokenRequest.account = account;
489
+ }
490
+
491
+ const getToken = async () => {
492
+ logger.verbose("useMsalAuthentication - Calling acquireTokenSilent");
493
+ return instance.acquireTokenSilent(tokenRequest).catch(async e => {
494
+ if (e instanceof msalBrowser.InteractionRequiredAuthError) {
495
+ if (!interactionInProgress.current) {
496
+ logger.error("useMsalAuthentication - Interaction required, falling back to interaction");
497
+ return login(fallbackInteractionType, tokenRequest);
498
+ } else {
499
+ logger.error("useMsalAuthentication - Interaction required but is already in progress. Please try again, if needed, after interaction completes.");
500
+ throw ReactAuthError.createUnableToFallbackToInteractionError();
501
+ }
502
+ }
503
+
504
+ throw e;
505
+ });
506
+ };
507
+
508
+ return getToken().then(response => {
509
+ setResponse([response, null]);
510
+ return response;
511
+ }).catch(e => {
512
+ setResponse([null, e]);
513
+ throw e;
514
+ });
515
+ }, [instance, interactionType, authenticationRequest, logger, account, login]);
367
516
  React.useEffect(() => {
368
517
  const callbackId = instance.addEventCallback(message => {
369
518
  switch (message.eventType) {
@@ -393,18 +542,27 @@ function useMsalAuthentication(interactionType, authenticationRequest, accountId
393
542
  };
394
543
  }, [instance, logger]);
395
544
  React.useEffect(() => {
396
- if (!hasBeenCalled && !error && !isAuthenticated && inProgress === msalBrowser.InteractionStatus.None) {
397
- logger.info("useMsalAuthentication - No user is authenticated, attempting to login"); // Ensure login is only called one time from within this hook, any subsequent login attempts should use the callback returned
398
-
399
- setHasBeenCalled(true);
400
- login().catch(() => {
401
- // Errors are handled by the event handler above
402
- return;
403
- });
545
+ if (shouldAcquireToken.current && inProgress === msalBrowser.InteractionStatus.None) {
546
+ shouldAcquireToken.current = false;
547
+
548
+ if (!isAuthenticated) {
549
+ logger.info("useMsalAuthentication - No user is authenticated, attempting to login");
550
+ login().catch(() => {
551
+ // Errors are saved in state above
552
+ return;
553
+ });
554
+ } else if (account) {
555
+ logger.info("useMsalAuthentication - User is authenticated, attempting to acquire token");
556
+ acquireToken().catch(() => {
557
+ // Errors are saved in state above
558
+ return;
559
+ });
560
+ }
404
561
  }
405
- }, [isAuthenticated, inProgress, error, hasBeenCalled, login, logger]);
562
+ }, [isAuthenticated, account, inProgress, login, acquireToken, logger]);
406
563
  return {
407
564
  login,
565
+ acquireToken,
408
566
  result,
409
567
  error
410
568
  };
@@ -419,16 +577,17 @@ function useMsalAuthentication(interactionType, authenticationRequest, accountId
419
577
  * @param props
420
578
  */
421
579
 
422
- function MsalAuthenticationTemplate({
423
- interactionType,
424
- username,
425
- homeAccountId,
426
- localAccountId,
427
- authenticationRequest,
428
- loadingComponent: LoadingComponent,
429
- errorComponent: ErrorComponent,
430
- children
431
- }) {
580
+ function MsalAuthenticationTemplate(_ref) {
581
+ let {
582
+ interactionType,
583
+ username,
584
+ homeAccountId,
585
+ localAccountId,
586
+ authenticationRequest,
587
+ loadingComponent: LoadingComponent,
588
+ errorComponent: ErrorComponent,
589
+ children
590
+ } = _ref;
432
591
  const accountIdentifier = React.useMemo(() => {
433
592
  return {
434
593
  username,