42

I has this error when trying to loginRedirect in React app using @azure/msal-react@1.0.0-alpha.6 and @azure/msal-browser@2.11.2. The login data returns correctly but the exception is raised in the console.

Uncaught (in promise) BrowserAuthError: interaction_in_progress: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API.

import * as msal from "@azure/msal-browser";

const msalConfig = {
  auth: {
      clientId: '995e81d0-',
      authority: 'https://login.microsoftonline.com/3a0cf09b-',
      redirectUri: 'http://localhost:3000/callback'
  },
  cache: {
    cacheLocation: "sessionStorage", // This configures where your cache will be stored
    storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
  }
};
const msalInstance = new msal.PublicClientApplication(msalConfig);
try {
  msalInstance.handleRedirectPromise()
    .then(res=>{
      console.log(res)
    })
    .catch(err => {
      console.error(err);
    });

  var loginRequest = {
    scopes: ["api://58ca819e-/access_as_user"] // optional Array<string>
  };
  msalInstance.loginRedirect(loginRequest);
} catch (err) {
  // handle error
  console.log(err)
}

The exception

Uncaught (in promise) BrowserAuthError: interaction_in_progress: Interaction is currently in progress. Please ensure that this interaction has been completed before calling an interactive API.
    at BrowserAuthError.AuthError [as constructor] (http://localhost:3000/static/js/vendors~main.chunk.js:852:20)
    at new BrowserAuthError (http://localhost:3000/static/js/vendors~main.chunk.js:8943:24)
    at Function.BrowserAuthError.createInteractionInProgressError (http://localhost:3000/static/js/vendors~main.chunk.js:9023:12)
    at PublicClientApplication.ClientApplication.preflightInteractiveRequest (http://localhost:3000/static/js/vendors~main.chunk.js:13430:30)
    at PublicClientApplication.<anonymous> (http://localhost:3000/static/js/vendors~main.chunk.js:12581:33)
    at step (http://localhost:3000/static/js/vendors~main.chunk.js:215:17)
    at Object.next (http://localhost:3000/static/js/vendors~main.chunk.js:146:14)
    at http://localhost:3000/static/js/vendors~main.chunk.js:118:67
    at new Promise (<anonymous>)
    at __awaiter (http://localhost:3000/static/js/vendors~main.chunk.js:97:10)
    at PublicClientApplication.ClientApplication.acquireTokenRedirect (http://localhost:3000/static/js/vendors~main.chunk.js:12565:12)
    at PublicClientApplication.<anonymous> (http://localhost:3000/static/js/vendors~main.chunk.js:13760:16)
    at step (http://localhost:3000/static/js/vendors~main.chunk.js:215:17)
    at Object.next (http://localhost:3000/static/js/vendors~main.chunk.js:146:14)
    at http://localhost:3000/static/js/vendors~main.chunk.js:118:67
    at new Promise (<anonymous>)
    at __awaiter (http://localhost:3000/static/js/vendors~main.chunk.js:97:10)
    at PublicClientApplication.loginRedirect (http://localhost:3000/static/js/vendors~main.chunk.js:13755:12)
    at Module.<anonymous> (http://localhost:3000/static/js/main.chunk.js:192:16)
    at Module../src/App.tsx (http://localhost:3000/static/js/main.chunk.js:292:30)
    at __webpack_require__ (http://localhost:3000/static/js/bundle.js:857:31)
    at fn (http://localhost:3000/static/js/bundle.js:151:20)
    at Module.<anonymous> (http://localhost:3000/static/js/main.chunk.js:2925:62)
    at Module../src/index.tsx (http://localhost:3000/static/js/main.chunk.js:3028:30)
    at __webpack_require__ (http://localhost:3000/static/js/bundle.js:857:31)
    at fn (http://localhost:3000/static/js/bundle.js:151:20)
    at Object.1 (http://localhost:3000/static/js/main.chunk.js:3570:18)
    at __webpack_require__ (http://localhost:3000/static/js/bundle.js:857:31)
    at checkDeferredModules (http://localhost:3000/static/js/bundle.js:46:23)
    at Array.webpackJsonpCallback [as push] (http://localhost:3000/static/js/bundle.js:33:19)
    at http://localhost:3000/static/js/main.chunk.js:1:67
beewest
  • 4,486
  • 7
  • 36
  • 63

9 Answers9

36
msalInstance.loginRedirect(loginRequest);

The piece of code above does next:

  1. Looks into session storage for key msal.[clientId].interaction.status and other temp values required for redirection process. If such key exist and its value equals 'interaction_in_progress' error will be thrown.
  2. Creates entry in session storage msal.[clientId].interaction.status = interaction.status
  3. Redirects user to auth-page.

In case of successful login user will be redirected to initial page with your code and go through 1-3 steps and will catch an error;

The piece of code below removes all temp values in session storage and completes auth redirection flow but it is async and never will be completed.

   msalInstance.handleRedirectPromise()
    .then(res=>{
      console.log(res)
    })
    .catch(err => {
      console.error(err);
    });

The solution will be

// Account selection logic is app dependent. Adjust as needed for different use cases.
// Set active acccount on page load
const accounts = msalInstance.getAllAccounts();
if (accounts.length > 0) {
  msalInstance.setActiveAccount(accounts[0]);
}

msalInstance.addEventCallback((event) => {
  // set active account after redirect
  if (event.eventType === EventType.LOGIN_SUCCESS && event.payload.account) {
    const account = event.payload.account;
    msalInstance.setActiveAccount(account);
  }
}, error=>{
  console.log('error', error);
});

console.log('get active account', msalInstance.getActiveAccount());

// handle auth redired/do all initial setup for msal
msalInstance.handleRedirectPromise().then(authResult=>{
  // Check if user signed in 
  const account = msalInstance.getActiveAccount();
  if(!account){
    // redirect anonymous user to login page 
    msalInstance.loginRedirect();
  }
}).catch(err=>{
  // TODO: Handle errors
  console.log(err);
});
  • 23
    I don't see a single mention of anything like this in the MS docs. It seems ridiculous to have to come to Stackoverflow in order to get the most basic thing working. Thanks for posting this... however the heck you miraculously came up with this! – dapug Oct 16 '21 at 08:45
  • 2
    Here is the hint: https://learn.microsoft.com/en-us/azure/active-directory/develop/msal-js-initializing-client-applications – rubeonline Oct 26 '21 at 12:41
  • 3
    @dapug - I was reading source code on GitHub, cause I had the same problem. – shevchenko-vladyslav Oct 27 '21 at 13:11
  • 5
    Still not working for me. Getting the OP's error still. Trying to simply implement Azure AD B2C on a NextJS app and it's the hardest thing I've done in over a year. – Alexandre Moreira Jun 22 '22 at 10:30
  • 13
    I can't believe how ridiculously complex MS has made it to implement B2C auth. There are dozens of different documentation pages and code samples with different and conflicting information. And different random solutions on the internet for figuring out the most basic functionality. Yet none of it completely works. I *really* want to switch to Auth0 or Cognito. This sucks!!!!!!!!!!!! – suntzu Jul 04 '22 at 23:10
  • Did you guys come up with a solution for NextJS? The number of hours i've spent on the docs gives me a heartache everytime. – SthaSAM May 27 '23 at 15:30
21

I believe this is the correct answer and way to set this up. Others here led me to clues to solve this.

TLDR; set your code up like this:

// authRedir.ts  (or authRedir.vue inside mounted())
await msalInstance.handleRedirectPromise();

// mySignInPage.ts (or userprofile.vue, or whatever page invokes a sign-in)
await msalInstance.handleRedirectPromise();

async signIn(){
  const loginRequest: msal.RedirectRequest = {
    scopes: ["openid", "profile", "offline_access","your_other_scopes"]
    redirectUri: "http://localhost:8080/authredirect"
        };

  const accounts = msalInstance.getAllAccounts();
  if (accounts.length === 0) {

    await msalInstance.loginRedirect();
  }
}

If you do this correctly, you wont need the code @shevchenko-vladislav shared, wherein setActiveAccount() has to be manually done by you. Remember to verify all async/await wherever you call this in your app! And notice how I did NOT use handleRedirectPromise().then() or anything, really, in my main authredirect.vue file. Just handleRedirectPromise() on load.

Other solutions on Stackoverflow suggest things like checking for and deleting the interaction state from the session. Um, no! If you have that state left over after a sign-in, it means the process wasn't done right! MSAL cleans itself up!

Full details:

It is super important to understand what MSAL is actually doing during it's entire lifecycle (especially the redir path as opposed to popup), and sadly the docs fail to do a good job. I found this little "side note" extremely, extremely important:

https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/errors.md#interaction_in_progress

"If you are calling loginRedirect or acquireTokenRedirect from a page that is not your redirectUri you will need to ensure handleRedirectPromise is called and awaited on both the redirectUri page as well as the page that you initiated the redirect from. This is because the redirectUri page will initiate a redirect back to the page that originally invoked loginRedirect and that page will process the token response."

In other words, BOTH your Redirect page AND the page that INVOKED the sign-in request MUST call handleRedirectPromise() on page load (or on mounted(), in my case, since I am using Vue)

In my case, I have this:

  • http://localhost:8080/authredirect *
  • http://localhost:8080/userprofile

*Only my AuthRedirect Uri needs to be registered as a RedirectUri with my app registration in Azure AD.

So here is the loginRedirect() lifecycle, which I had NO idea, and lost a days work sorting out:

  1. /UserProfile (or some page) invokes a sign-in request
  2. The request calls handleRedirectPromise() (which sets MSAL up with info about where the request was made AND the interaction state that will bite you later if you dont complete the process)
  3. and THEN calls loginRedirect(loginRequest)
  4. -> user is redirected, completes sign-in
  5. Azure redir back to -> /AuthRedirect
  6. /AuthRedirect invokes handleRedirectPromise(), which forwards along to -> /UserProfile
  7. /UserProfile invokes handleRedirectPromise() which does the actual processing of tokens AND internally calls setActiveAccount() to save your user to session.

Dang. That was fun. And not explained in the docs AT ALL.

So, the reason you are getting the interaction-in-progress error is because you are thinking you're all done on step 6. NOPE! Step 7 is where that interaction_in_progress state gets settled and cleaned up so that subsequent calls wont trip up on it!!

Final thought:

If you have a designated sign-in page you want users to always start/finish from (and itself is the registered redirect Uri), I suppose these steps will be reduced (no forwarding like in step 6 here). In my case, I want the user redirected back to wherever they might have gotten bumped out of due to a session expiration. So I just found it easier to call handleRedirectPromise() on every single page load everywhere, in case said page it needs to finalize authentication. Or, I could build my own redirect logic into a dedicated sign-in page that can put the user back where they were prior to hitting it. It's just that as for MSAL, I had NO idea the process was finishing up on the requesting page rather than contained within my AuthRedirect page, and THAT is what bit me.

Now, if we could just get MS to provide far better docs on the delicate and critical nature of MSAL, and to provide a Vue plugin (why does only Angular and React get all the glory? :) ), that would be great!

dapug
  • 1,781
  • 4
  • 22
  • 28
  • 1
    Wow, this is so simple and it worked. All I had to do in my angular app was add the line "this.msalService.instance.handleRedirectPromise();" in the ngOnInit() method and it worked! – Caustix Aug 27 '22 at 21:50
  • 1
    This person also covers this for angular apps 16 minutes in https://www.youtube.com/watch?v=TkCKqeYjpv0 – Caustix Aug 27 '22 at 21:59
  • Saved my sanity with this one, thank you, dapug. Had to tweak it slightly for my use case. – Thomas May 27 '23 at 12:42
13

You can clear the browser storage before open the loginPopup:

let msalInstance: PublicClientApplication = this._msauthService.instance as PublicClientApplication;
msalInstance["browserStorage"].clear();
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
PeterReimer
  • 131
  • 1
  • 3
11

During development, it is possible that you left the sign-in flow in a progress-state due to a coding issue that you will need to correct. You can clear the immediate problem by deleting the msal.interaction.status cookie from the browser. Of course, if this problem persists, then you need to correct the problem using one of the other solutions suggested on this page.

jmig
  • 111
  • 1
  • 3
  • 3
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 16 '21 at 21:20
  • I came here to make the same comment jmig made above. Deleting the cookies got it going for me. I'm intrigued by dapug's solution, which makes sense but is far from what's recommended in the documentation. I haven't needed to paste handleRedirectPromise() all round my codebase - so far at least! – Mick Buckley Oct 07 '22 at 01:53
  • In case it helps anyone, I think the interaction-in-progress status got into my cookie because I mistakenly set 'navigateToLoginRequestUrl: true' at one point. – Mick Buckley Oct 07 '22 at 03:59
  • To clarify further, I haven't needed to call handleRedirectPromise because it's done for me. I'm using msal-react (v1.0.1). It calls handleRedirectPromise in a useEffect hook inside MsalProvider. – Mick Buckley Oct 09 '22 at 21:16
0

I have found that in msal.js v2 you can check interaction status in vanilla .js code to see if there is an interaction in progress, should you need to do this for some reason:

const publicClientApplication = new window.msal.PublicClientApplication(msalConfig);

var clientString = "msal." + msalConfig.clientId + ".interaction.status";

var interaction-status = publicClientApplication.browserStorage.temporaryCacheStorage.windowStorage[clientString]
GrahamJ
  • 528
  • 4
  • 15
0

Update @azure/msal-browser@2.21.0.

Rets
  • 1
  • 1
0

For folks with an Azure/Active Directory situation:

My issue wasn't with my code. It was with deactivating the "Access tokens (used for implicit flows)" setting found in the Active Directory > Authentication > Implicit grant and hybrid flows section.

After you put the proper Redirect URIs into the Web section:

ex: https://example.com/.auth/login/aad/callback

And after you put the proper Redirect URIs into the Single-page application section:

ex: https://example.com

ex: https://localhost:4200

The last step is to make sure you disable the Access tokens I mentioned in the beginning:

Disable the Access tokens setting found in Implicit grant and hybrid flows section in the Active Directory Authentication

When I was migrating my apps from .NET5 to .NET6 and the prior Angular Authentication over to MSAL, this setting was already checked for me (both were checked). After unchecking this setting, everything ended up working.

LatentDenis
  • 2,839
  • 12
  • 48
  • 99
-1

This may not be a clean solution. But this does work at least in Vue.js.

Next to your acquireToken() logic, add this

// Check Local or Session storage which may have already contain key 
// that partially matches your Azure AD Client ID
let haveKeys = Object.keys(localStorage).toString().includes('clientId')
// That error will just go away when you refrest just once
let justOnce = localStorage.getItem("justOnce");

if (haveKeys && !justOnce) {
  localStorage.setItem("justOnce", "true");
  window.location.reload();
} else {
  localStorage.removeItem("justOnce")
}
hubert17
  • 285
  • 5
  • 8
-1

I have faced the similar error in my project.I took reference of the below link. It takes hardly 10 minutes to go through it. It will surely resolve if you face the scenarios given in it. Link: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-angular/docs/v2-docs/redirects.md

  • 1
    Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Aug 12 '22 at 08:06