I has this error when trying to loginRedirect in React app using @azure/[email protected] and @azure/[email protected]. 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
The MSAL library for JavaScript enables client-side JavaScript applications to authenticate users using Azure AD work and school accounts (AAD), Microsoft personal accounts (MSA) and social identity providers like Facebook, Google, LinkedIn, Microsoft accounts, etc. through Azure AD B2C service.
In MSAL, you can get access tokens for the APIs your app needs to call using the acquireTokenSilent method which makes a silent request(without prompting the user with UI) to Azure AD to obtain an access token.
MSAL React enables React 16+ applications to authenticate enterprise users by using Azure Active Directory (Azure AD), and also users with Microsoft accounts and social identities like Facebook, Google, and LinkedIn. The library also enables applications to get access to Microsoft cloud services and Microsoft Graph.
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!!
If it is not handled correctly, and login is started again (pressing the button again), an interaction_in_progress error will occur. Here are some steps you can try to mitigate: Upgrade to the latest version of msal-angular. You can do this by uninstalling msal-angular, and reinstalling by using npm install @azure/msal-angular@alpha
However, my HomeComponent makes an API call to a protected resource onInit, which was then triggering the MsalInterceptor and trying to kick off another authentication flow, thus resulting in the interaction_in_progress.
msalInstance.loginRedirect(loginRequest);
The piece of code above does next:
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);
});
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
oracquireTokenRedirect
from a page that is not yourredirectUri
you will need to ensurehandleRedirectPromise
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 invokedloginRedirect
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:
*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:
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)loginRedirect(loginRequest)
handleRedirectPromise()
, which forwards along to -> /UserProfilehandleRedirectPromise()
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!
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.
You can clear the browser storage before open the loginPopup:
let msalInstance: PublicClientApplication = this._msauthService.instance as PublicClientApplication;
msalInstance["browserStorage"].clear();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With